Purpose

  1. Build a weighted and directed network of genes based on the Dual RNA seq data generated from the timecourse of Phytophthora cinnamomi and Lupinus angustifolius
  2. Determine modules (clusters of genes) from the network associated to disease progression (for example, biotrophic or necrotrophic stages)
  3. Extract the names of the genes from these hubs and compare them to the genes in the literature
  4. Functionally annotate these genes

Working idea: This network will then be used to identify hub genes for each of the early, middle and late interactions.

Load Libraries

Load Phytophthora cinnamomi gtf file, clean up to get Gene IDs

Combine alternatively spliced transcripts (seen in transcript_id as -RA and RB)

suppressPackageStartupMessages({c(
library(WGCNA),
library(DESeq2),
library(GEOquery),
library(tidyverse),
library(gridExtra),
library(reshape2),
library(rtracklayer),
library(dplyr),
library(edgeR))})
  [1] "WGCNA"                "fastcluster"          "dynamicTreeCut"       "stats"                "graphics"            
  [6] "grDevices"            "utils"                "datasets"             "methods"              "base"                
 [11] "DESeq2"               "SummarizedExperiment" "Biobase"              "MatrixGenerics"       "matrixStats"         
 [16] "GenomicRanges"        "GenomeInfoDb"         "IRanges"              "S4Vectors"            "BiocGenerics"        
 [21] "stats4"               "WGCNA"                "fastcluster"          "dynamicTreeCut"       "stats"               
 [26] "graphics"             "grDevices"            "utils"                "datasets"             "methods"             
 [31] "base"                 "GEOquery"             "DESeq2"               "SummarizedExperiment" "Biobase"             
 [36] "MatrixGenerics"       "matrixStats"          "GenomicRanges"        "GenomeInfoDb"         "IRanges"             
 [41] "S4Vectors"            "BiocGenerics"         "stats4"               "WGCNA"                "fastcluster"         
 [46] "dynamicTreeCut"       "stats"                "graphics"             "grDevices"            "utils"               
 [51] "datasets"             "methods"              "base"                 "lubridate"            "forcats"             
 [56] "stringr"              "dplyr"                "purrr"                "readr"                "tidyr"               
 [61] "tibble"               "ggplot2"              "tidyverse"            "GEOquery"             "DESeq2"              
 [66] "SummarizedExperiment" "Biobase"              "MatrixGenerics"       "matrixStats"          "GenomicRanges"       
 [71] "GenomeInfoDb"         "IRanges"              "S4Vectors"            "BiocGenerics"         "stats4"              
 [76] "WGCNA"                "fastcluster"          "dynamicTreeCut"       "stats"                "graphics"            
 [81] "grDevices"            "utils"                "datasets"             "methods"              "base"                
 [86] "gridExtra"            "lubridate"            "forcats"              "stringr"              "dplyr"               
 [91] "purrr"                "readr"                "tidyr"                "tibble"               "ggplot2"             
 [96] "tidyverse"            "GEOquery"             "DESeq2"               "SummarizedExperiment" "Biobase"             
[101] "MatrixGenerics"       "matrixStats"          "GenomicRanges"        "GenomeInfoDb"         "IRanges"             
[106] "S4Vectors"            "BiocGenerics"         "stats4"               "WGCNA"                "fastcluster"         
[111] "dynamicTreeCut"       "stats"                "graphics"             "grDevices"            "utils"               
[116] "datasets"             "methods"              "base"                 "reshape2"             "gridExtra"           
[121] "lubridate"            "forcats"              "stringr"              "dplyr"                "purrr"               
[126] "readr"                "tidyr"                "tibble"               "ggplot2"              "tidyverse"           
[131] "GEOquery"             "DESeq2"               "SummarizedExperiment" "Biobase"              "MatrixGenerics"      
[136] "matrixStats"          "GenomicRanges"        "GenomeInfoDb"         "IRanges"              "S4Vectors"           
[141] "BiocGenerics"         "stats4"               "WGCNA"                "fastcluster"          "dynamicTreeCut"      
[146] "stats"                "graphics"             "grDevices"            "utils"                "datasets"            
[151] "methods"              "base"                 "rtracklayer"          "reshape2"             "gridExtra"           
[156] "lubridate"            "forcats"              "stringr"              "dplyr"                "purrr"               
[161] "readr"                "tidyr"                "tibble"               "ggplot2"              "tidyverse"           
[166] "GEOquery"             "DESeq2"               "SummarizedExperiment" "Biobase"              "MatrixGenerics"      
[171] "matrixStats"          "GenomicRanges"        "GenomeInfoDb"         "IRanges"              "S4Vectors"           
[176] "BiocGenerics"         "stats4"               "WGCNA"                "fastcluster"          "dynamicTreeCut"      
[181] "stats"                "graphics"             "grDevices"            "utils"                "datasets"            
[186] "methods"              "base"                 "rtracklayer"          "reshape2"             "gridExtra"           
[191] "lubridate"            "forcats"              "stringr"              "dplyr"                "purrr"               
[196] "readr"                "tidyr"                "tibble"               "ggplot2"              "tidyverse"           
[201] "GEOquery"             "DESeq2"               "SummarizedExperiment" "Biobase"              "MatrixGenerics"      
[206] "matrixStats"          "GenomicRanges"        "GenomeInfoDb"         "IRanges"              "S4Vectors"           
[211] "BiocGenerics"         "stats4"               "WGCNA"                "fastcluster"          "dynamicTreeCut"      
[216] "stats"                "graphics"             "grDevices"            "utils"                "datasets"            
[221] "methods"              "base"                 "edgeR"                "limma"                "rtracklayer"         
[226] "reshape2"             "gridExtra"            "lubridate"            "forcats"              "stringr"             
[231] "dplyr"                "purrr"                "readr"                "tidyr"                "tibble"              
[236] "ggplot2"              "tidyverse"            "GEOquery"             "DESeq2"               "SummarizedExperiment"
[241] "Biobase"              "MatrixGenerics"       "matrixStats"          "GenomicRanges"        "GenomeInfoDb"        
[246] "IRanges"              "S4Vectors"            "BiocGenerics"         "stats4"               "WGCNA"               
[251] "fastcluster"          "dynamicTreeCut"       "stats"                "graphics"             "grDevices"           
[256] "utils"                "datasets"             "methods"              "base"                

Load GO terms blasted from Omicsbox

pc.gtf <- rtracklayer::import("https://ftp.ncbi.nlm.nih.gov/genomes/genbank/protozoa/Phytophthora_cinnamomi/latest_assembly_versions/GCA_018691715.1_ASM1869171v1/GCA_018691715.1_ASM1869171v1_genomic.gtf.gz")
trying URL 'https://ftp.ncbi.nlm.nih.gov/genomes/genbank/protozoa/Phytophthora_cinnamomi/latest_assembly_versions/GCA_018691715.1_ASM1869171v1/GCA_018691715.1_ASM1869171v1_genomic.gtf.gz'
Content type 'application/x-gzip' length 2722947 bytes (2.6 MB)
==================================================
downloaded 2.6 MB
pc.df <- as.data.frame((pc.gtf))

pc.genes <- filter(pc.df, type == "start_codon")
pc.geneids <- select(pc.genes, gene_id, product, protein_id)  

pc.annotations <- pc.geneids %>% 
  distinct(gene_id, .keep_all = T)
nrow(pc.annotations)
[1] 19969
pcannot <- as.matrix(pc.annotations)

#write.table(pcannot, file = "pcgeneids.tsv",quote=FALSE,sep="\t")

Make GO term list for enrichment analysis

goterms <- read.delim('pc.goterms.txt', header = TRUE) %>%
  select(c(SeqName, GO.IDs, GO.Names, Description))
  
head(goterms)

colnames(goterms) <- c("protein_id", "GO_terms", "GO_descripton", "description")

goterms.df <- dplyr::right_join(goterms, pc.annotations, by = 'protein_id')
nrow(goterms.df)
[1] 19969

Assemble count matrix and coldata file

Warning: `as.tibble()` was deprecated in tibble 2.0.0.
Please use `as_tibble()` instead.
The signature and semantics have changed, see `?as_tibble`.
 [1] "F:GO:0005515" "P:GO:0015074" "F:GO:0003676" "F:GO:0008270" "P:GO:0006355" "F:GO:0003700" "F:GO:0005509" "P:GO:0036297"
 [9] "C:GO:0043240" "P:GO:0006629" "F:GO:0020037" "P:GO:0005975" "P:GO:0006508" "F:GO:0030248" "C:GO:0005576" "P:GO:0006801"
[17] "F:GO:0046872" "F:GO:0005525" "P:GO:0007076" "C:GO:0016020"
[1] "F:GO:0005515 F:protein binding"                           "P:GO:0015074 P:DNA integration"                          
[3] "F:GO:0003676 F:nucleic acid binding"                      "F:GO:0008270 F:zinc ion binding"                         
[5] "P:GO:0006355 P:regulation of DNA-templated transcription" "F:GO:0003700 F:DNA-binding transcription factor activity"

Import files into r

tmp<-read.table("3col.tsv.gz",header=F)
x<-as.matrix(acast(tmp, V2~V1, value.var="V3"))
colnames(x)<-sapply(strsplit(colnames(x),"_"),"[[",1)

#head(x)
#write.table(x,file="countmatrix.tsv",quote=FALSE,sep="\t")

Clean countmatrix for P. cinnamomi analysis

coldata <- read.table('sample_info.tsv')
Pcgenenames <- read.table('pcgeneids.tsv', row.names = 1, quote = "", sep='\t', fill = TRUE, header = TRUE)
IUM83Tanjilcountmatrix <- read.table('countmatrix.tsv') 

Clean coldata for Deseq2 normalisation

collabels <- colnames(IUM83Tanjilcountmatrix) <- (coldata$Sample)
colnames(IUM83Tanjilcountmatrix) <- collabels

IUM83Tanjilcountmatrix <- tibble::rownames_to_column(IUM83Tanjilcountmatrix, "gene_id")

IUM83CountMatrix <- IUM83Tanjilcountmatrix %>% 
  filter(str_detect(gene_id, "IUM83")) 
  
rownames(IUM83CountMatrix) <- IUM83CountMatrix$gene_id
IUM83CountMatrix <- IUM83CountMatrix [ ,-1]
IUM83CountMatrix <- as.data.frame(IUM83CountMatrix)
IUM83CountMatrix <- IUM83CountMatrix[ ,-(1:12)]

#write.table(IUM83CountMatrix, file = "Pc_countmatrix.tsv",quote=FALSE,sep="\t")

Detect and remove outlier genes

colData <- coldata %>% 
  filter(str_detect(Sample, "Pc"))

rownames(colData) <- colData$Sample
colData <- colData [ ,-1] 

Detect outlier samples

#Call a function from WGCNA package that detects outliers 
#Rows should be the samples and the columns genes
gsg <- goodSamplesGenes(t(IUM83CountMatrix))
 Flagging genes and samples with too many missing values...
  ..step 1
  ..step 2
summary(gsg)
            Length Class  Mode   
goodGenes   19981  -none- logical
goodSamples    12  -none- logical
allOK           1  -none- logical
#If false, then there are outliers
gsg$allOK #False
[1] FALSE
table(gsg$goodGenes) #3012to be excluded

FALSE  TRUE 
 3012 16969 
table(gsg$goodSamples) #All 12 samples passed

TRUE 
  12 
# remove genes that are detectd as outliers
data <- IUM83CountMatrix[gsg$goodGenes == TRUE,]

Normalisation using DESeq2 package

The WGCNA library requires normalisation using the vst (variance-stabilising transform) function from the DESeq2 package

# detect outlier samples - hierarchical clustering
htree <- hclust(dist(t(data)), method = "average")
groups <- cutree(htree, k=2) # cut tree into clusters

plot(htree, labels(groups))
# draw dendogram with red borders around the clusters
rect.hclust(htree, k=2, border="red")

Network Construction

# exclude outlier samples from the column data to match the new data
#colData <- colData %>% 
#  filter(!row.names(.) %in% outliers)

# making sure the rownames and column names identical for the two data sets
#all(rownames(colData) == colnames(clusters))

# create DESeq Data Set
dds <- DESeqDataSetFromMatrix(countData = data,
                              colData = colData,
                              design = ~ 1) # no model, for normalisation only

# remove all genes with counts < 10 in more than 90% of samples (12*0.9=10.8 ~ 11) as suggested by WGCNA on RNAseq FAQ (https://horvath.genetics.ucla.edu/html/CoexpressionNetwork/Rpackages/WGCNA/faq.html)
# <10 in more than 90% samples
dds90 <- dds[rowSums(counts(dds) >= 10) >= 11,]
nrow(dds90) # 5403 genes
[1] 5403
# >= 10) >= 11,] 5403 genes <===== #less than 10 counts in %90 of samples
# >= 15) >= 11,] 4420 genes 

gene.list <- row.names(dds90)
#lapply(gene.list, write, "all_genes.txt", append=TRUE)

# perform variance stabilization
dds_norm <- vst(dds90)
-- note: fitType='parametric', but the dispersion trend was not well captured by the
   function: y = a/x + b, and a local regression fit was automatically substituted.
   specify fitType='local' or 'mean' to avoid this message next time.
# get normalized counts
norm.counts <- assay(dds_norm) %>% 
  t()

your_dds <- estimateSizeFactors(dds90)
your_dds <- estimateDispersions(your_dds)
gene-wise dispersion estimates
mean-dispersion relationship
-- note: fitType='parametric', but the dispersion trend was not well captured by the
   function: y = a/x + b, and a local regression fit was automatically substituted.
   specify fitType='local' or 'mean' to avoid this message next time.
final dispersion estimates
# Plot dispersion estimates and fits
plotDispEsts(your_dds, main = "Dispersion Trend with Local and Parametric Fits")



#Save normalised dataset for faster analysis next time
#saveRDS(norm.counts, "norm.counts.rds")

Create network and identify modules

Note: this chunk is set not to run since the blockwise Modules function takes a long time to run. The R object has been saved and is loaded in the next chunk for a faster run time.

# convert matrix to numeric
norm.counts[] <- sapply(norm.counts, as.numeric)

#Successively, hierarchical clustering was performed to identify modules
temp_cor <- cor
cor <- WGCNA::cor

# Parameters to be tweaked later
bwnet <- blockwiseModules(norm.counts,
                 maxBlockSize = 7500, # selected total number of genes since CPU memory is sufficient (32GB workstation should be able to handle perhaps 30000)
                 TOMType = "signed",
                 power = soft_power,
                 mergeCutHeight = 0.10,
                 saveTOMs = TRUE,
                 saveTOMFileBase = "pc_TOM",
                 numericLabels = FALSE, # set as false to assign color as labels
                 #randomSeed = 1243, # for reproducibility since this function uses clustering
                 verbose = 3)
 Calculating module eigengenes block-wise from all genes
   Flagging genes and samples with too many missing values...
    ..step 1
 ..Working on block 1 .
    TOM calculation: adjacency..
    ..will not use multithreading.
     Fraction of slow calculations: 0.000000
    ..connectivity..
    ..matrix multiplication (system BLAS)..
    ..normalization..
    ..done.
   ..saving TOM for block 1 into file pc_TOM-block.1.RData
 ....clustering..
 ....detecting modules..
 ....calculating module eigengenes..
 ....checking kME in modules..
  ..reassigning 1 genes from module 2 to modules with higher KME.
  ..reassigning 1 genes from module 3 to modules with higher KME.
  ..reassigning 4 genes from module 5 to modules with higher KME.
 ..merging modules that are too close..
     mergeCloseModules: Merging modules whose distance is less than 0.1
       Calculating new MEs...
# Save bwnet object
saveRDS(bwnet, "bwnet.rds")

cor <- temp_cor

Relate the modules to stages

# load bwnet object
bwnet <- readRDS("bwnet.rds")

module_eigengenes <- bwnet$MEs %>%
  orderMEs(.)

head(module_eigengenes)


# Plot the dendrogram and the module colors before and after merging underneath
plotDendroAndColors(bwnet$dendrograms[[1]], cbind(bwnet$unmergedColors, bwnet$colors),
                    c("unmerged", "merged"),
                    dendroLabels = FALSE,
                    addGuide = TRUE,
                    hang= 0.03,
                    guideHang = 0.05)


 # grey module = all genes that doesn't fall into other modules
 
module.total <- table(bwnet$colors)
# Define numbers of genes and samples
nSamples <- nrow(norm.counts)
nGenes <- ncol(norm.counts)

# correlation for module eigengenes and traits
module.trait.corr <- cor(module_eigengenes, traits, use = 'p')
module.trait.corr.pvals <- corPvalueStudent(module.trait.corr, nSamples)

# Heat map v2 (from WGCNA tutorial)
textMatrix =  paste(signif(module.trait.corr, 2), "\n(",
                    signif(module.trait.corr.pvals, 1), ")", sep = "")
dim(textMatrix) = dim(module.trait.corr)



par(mar = c(6, 8.5, 3, 3));
labeledHeatmap(Matrix = module.trait.corr,
               xLabels = colnames(module.trait.corr),
               yLabels = rownames(module.trait.corr),
               ySymbols = rownames(module.trait.corr),
               colorLabels = FALSE,
               colors = blueWhiteRed(50),
               textMatrix = textMatrix,
               textAdj = c(0.5, 0.5),
               setStdMargins = FALSE,
               cex.lab.y = 0.6,
               cex.text = 0.7,
               zlim = c(-1,1),
               main = paste("Module-trait relationships"))




# get number of genes for each module
table(bwnet$colors)

       black         blue        brown         cyan        green  greenyellow         grey    lightcyan      magenta 
         243          881          524           57          262           95          428           40          132 
midnightblue         pink       purple          red       salmon          tan    turquoise       yellow 
          47          201           98          249           65           85         1681          315 
#Tag genes with module membership and store it in a table
module.gene.mapping <- as.data.frame(bwnet$colors)
module.gene.mapping
NA

Significance in brackets shows how significantly associated the module (cluster of genes) is the trait of interest
Find modules that have significant association with disease state

Calculate the module membership and the associated p-values The module membership/intramodular connectivity is calculated as the correlation of the eigengene and the gene expression profile. This quantifies the similarity of all genes on the array to every module.

Using the gene significance you can identify genes that have a high significance for trait of interest Using the module membership measures you can identify genes with high module membership in interesting modules.

Gene Significance at the Early Pc-lupin interaction

#create traits file - assign 1 if a sample is a certain stage, else assign 0
Dis_traits <- colData %>% 
  mutate(Dis.vs.all = ifelse(grepl('Treated', Treatment), 1, 0)) %>% 
  select(5)


factor_levels <- unique(colData$Stage)

# transform stages into factors and define levels
colData$Stage <- factor(colData$Stage, levels = factor_levels)

traits <- binarizeCategoricalColumns(colData$Stage,
                           #dropFirstLevelVsAll = FALSE,
                           includePairwise = FALSE,
                           includeLevelVsAll = TRUE,
                           minCount = 1)

traits <- cbind(Dis_traits, traits)
# Define a gene significance variable for Early
GS.Early <- as.numeric(cor(norm.counts, traits$data.Early.vs.all, use = "p"))

# This translates the numeric values into colors
GS.stage_earlyColor = numbers2colors(GS.Early, signed = T)
blocknumber = 1

moduleLabelsAutomatic = bwnet$colors
# Convert labels to colors for plotting
moduleColorsAutomatic = labels2colors(moduleLabelsAutomatic)

datColors = data.frame(bwnet$colors, GS.stage_earlyColor)[bwnet$blockGenes[[blocknumber]], 
    ]

# Plot the dendrogram and the module colors underneath
plotDendroAndColors(bwnet$dendrograms[[blocknumber]], colors = datColors, groupLabels = c("Module colors", 
    "GS.stage.Early"), dendroLabels = FALSE, hang = 0.03, addGuide = TRUE, guideHang = 0.05)

earlygs <- early.gene.signf.corr %>% 
  as.data.frame() %>%
  tibble::rownames_to_column("gene_id") 

earlygsid <- dplyr::right_join(pc.annotations, earlygs, by = 'gene_id')

datKme.id <- as.data.frame(datKME) %>%
  rownames_to_column("gene_id") 

earlyeiginengene <- dplyr::right_join(earlygsid, datKme.id, by = 'gene_id')

Gene Significance at the Mid Pc-lupin interaction

datKME <- signedKME(norm.counts, module_eigengenes)

# Measure the correlation of the gene's module membership and the associated p-values
module.membership.measure <- cor(module_eigengenes, norm.counts, use = 'p')
module.membership.measure.pvals <- corPvalueStudent(module.membership.measure, nSamples)

# Calculate the gene significance and associated p-values
early.gene.signf.corr <- cor(norm.counts, traits$data.Early.vs.all, use = 'p')
early.gene.signf.corr.pvals <- corPvalueStudent(early.gene.signf.corr, nSamples)


colorOfColumn = substring(names(datKME), 4)
#selectModules = c("brown", "magenta", "blue", "turquoise", "lightcyan")
selectModules = colorOfColumn[colorOfColumn !="grey"]

#par(mfrow = c(4, length(selectModules)/4))
  for (module in selectModules) {
      column = match(module, colorOfColumn)
      restModule = moduleColorsAutomatic == module
      verboseScatterplot(datKME[restModule, column], GS.Early[restModule], xlab = paste("Module Membership", 
          module, "module"), ylab = "GS.stage_Early", main = paste("kME.", module, 
          "vs. GS"), col = module)
  }

earlygs <- early.gene.signf.corr %>% 
  as.data.frame() %>%
  tibble::rownames_to_column("gene_id") 

earlygsid <- dplyr::right_join(pc.annotations, earlygs, by = 'gene_id')

datKme.id <- as.data.frame(datKME) %>%
  rownames_to_column("gene_id") 

earlyeiginengene <- dplyr::right_join(earlygsid, datKme.id, by = 'gene_id')
# Define a gene significance variable for Early
GS.mid <- as.numeric(cor(norm.counts, traits$data.Middle.vs.all, use = "p"))

# This translates the numeric values into colors
GS.stage_midColor = numbers2colors(GS.mid, signed = T)
blocknumber = 1

moduleLabelsAutomatic = bwnet$colors
# Convert labels to colors for plotting
moduleColorsAutomatic = labels2colors(moduleLabelsAutomatic)

datColors = data.frame(bwnet$colors, GS.stage_midColor)[bwnet$blockGenes[[blocknumber]], 
    ]

# Plot the dendrogram and the module colors underneath
plotDendroAndColors(bwnet$dendrograms[[blocknumber]], colors = datColors, groupLabels = c("Module colors", 
    "GS.mid"), dendroLabels = FALSE, hang = 0.03, addGuide = TRUE, guideHang = 0.05)

Gene Significance at the Late Pc-lupin interaction

datKME <- signedKME(norm.counts, module_eigengenes)

# Measure the correlation of the gene's module membership and the associated p-values
module.membership.measure <- cor(module_eigengenes, norm.counts, use = 'p')
module.membership.measure.pvals <- corPvalueStudent(module.membership.measure, nSamples)

# Calculate the gene significance and associated p-values
mid.gene.signf.corr <- cor(norm.counts, traits$data.Middle.vs.all, use = 'p')
mid.gene.signf.corr.pvals <- corPvalueStudent(mid.gene.signf.corr, nSamples)


colorOfColumn = substring(names(datKME), 4)
#selectModules = c("red", "tan", "salmon","green", "magenta")
selectModules = colorOfColumn[colorOfColumn !="grey"]

#par(mfrow = c(3, length(selectModules)/3))
  for (module in selectModules) {
      column = match(module, colorOfColumn)
      restModule = moduleColorsAutomatic == module
      verboseScatterplot(datKME[restModule, column], GS.mid[restModule], xlab = paste("Module Membership", 
          module, "module"), ylab = "GS.stage_mid", main = paste("kME.", module, 
          "vs. GS"), col = module)
  }

midgs <- mid.gene.signf.corr %>% 
  as.data.frame() %>%
  tibble::rownames_to_column("gene_id") 

midgsid <- dplyr::right_join(Pcgenenames, midgs, by = 'gene_id')

datKme.id <- as.data.frame(datKME) %>%
  rownames_to_column("gene_id") 

mideiginengene <- dplyr::right_join(midgsid, datKme.id, by = 'gene_id')

turquoise module (Horvath cares not for the reported cor=). Instead, Look at the y-axis: genes that have high positive module membership tend to be highly positively correlated with the late interaction of Pc in lupin, whereas, genes with negative values in the module they have negative relations with the late interaction of Pc in lupin. Can select either; genes with high positive/negative GS or high kMe (hub genes)

# Define a gene significance variable for Early
GS.late <- as.numeric(cor(norm.counts, traits$data.Late.vs.all, use = "p"))

# This translates the numeric values into colors
GS.stage_lateColor = numbers2colors(GS.late, signed = T)
blocknumber = 1

moduleLabelsAutomatic = bwnet$colors
# Convert labels to colors for plotting
moduleColorsAutomatic = labels2colors(moduleLabelsAutomatic)

datColors = data.frame(bwnet$colors, GS.stage_lateColor)[bwnet$blockGenes[[blocknumber]], 
    ]

# Plot the dendrogram and the module colors underneath
plotDendroAndColors(bwnet$dendrograms[[blocknumber]], colors = datColors, groupLabels = c("Module colors", 
    "GS.stage.Late"), dendroLabels = FALSE, hang = 0.03, addGuide = TRUE, guideHang = 0.05)

datKME <- signedKME(norm.counts, module_eigengenes)

# Measure the correlation of the gene's module membership and the associated p-values
module.membership.measure <- cor(module_eigengenes, norm.counts, use = 'p')
module.membership.measure.pvals <- corPvalueStudent(module.membership.measure, nSamples)

# Calculate the gene significance and associated p-values
late.gene.signf.corr <- cor(norm.counts, traits$data.Late.vs.all, use = 'p')
late.gene.signf.corr.pvals <- corPvalueStudent(late.gene.signf.corr, nSamples)


colorOfColumn = substring(names(datKME), 4)

#selectModules = c("green", "turquoise", "blue", "grey60", "salmon")
selectModules = colorOfColumn[colorOfColumn !="grey"]

#par(mfrow = c(3, length(selectModules)/3))
  for (module in selectModules) {
      column = match(module, colorOfColumn)
      restModule = moduleColorsAutomatic == module
      verboseScatterplot(datKME[restModule, column], GS.late[restModule], xlab = paste("Module Membership", 
          module, "module"), ylab = "GS.stage_Late", main = paste("kME.", module, 
          "vs. GS"), col = module)
  }

lategs <- late.gene.signf.corr.pvals %>% 
  as.data.frame() %>%
  tibble::rownames_to_column("gene_id")

lategsid <- dplyr::right_join(Pcgenenames, lategs, by = 'gene_id')

datKme.id <- as.data.frame(datKME) %>%
  rownames_to_column("gene_id") 

lateeiginengene <- dplyr::right_join(lategsid, datKme.id, by = 'gene_id')
library("clusterProfiler")
library("kableExtra")

writeGMT <- function (object, fname ){
  if (class(object) != "list") stop("object should be of class 'list'")
  if(file.exists(fname)) unlink(fname)
  for (iElement in 1:length(object)){
    write.table(t(c(make.names(rep(names(object)[iElement],2)),object[[iElement]])),
                sep="\t",quote=FALSE,
                file=fname,append=TRUE,col.names=FALSE,row.names=FALSE)
  }
}

writeGMT(object=gul,fname="goterms.gmt")
genesets <- read.gmt("goterms.gmt")
bg <- gene.list
# chooseTopHubInEachModule returns the gene in each module with the highest connectivity, looking at all genes in the expression file

PcHubgenes <- chooseTopHubInEachModule(norm.counts,
                         colorh = module.gene.mapping,
                         omitColor = "grey",
                         power =  2,
                         type = "signed")
PcHubgenes 
        black          blue         brown          cyan         green   greenyellow     lightcyan       magenta  midnightblue 
"IUM83_03164" "IUM83_12926" "IUM83_06000" "IUM83_08664" "IUM83_06699" "IUM83_02874" "IUM83_10668" "IUM83_04177" "IUM83_09489" 
         pink        purple           red        salmon           tan     turquoise        yellow 
"IUM83_14330" "IUM83_02522" "IUM83_07133" "IUM83_02405" "IUM83_19502" "IUM83_15977" "IUM83_18642" 
PcHubgenes.df <- PcHubgenes %>% 
  as.data.frame(row.names = NULL, stringAsFactors = FALSE) 

PcHubgenes.df <- PcHubgenes.df %>% 
       rename("gene_id" = ".")

PcHubgenes.df <- tibble::rownames_to_column(PcHubgenes.df, "module")


PcHubgenes.df <- dplyr::right_join(pc.annotations, PcHubgenes.df, by = 'gene_id')
PcHubgenes.df
# Heatmap of old module eigen-genes and samples
#pdf(file="oldMEs.pdf",heigh=80,width=20)

library("pheatmap")
library(RColorBrewer)

MEs <- bwnet$MEs
rownames(MEs)=names(norm.counts[,9])
pheatmap(MEs,cluster_col=T,cluster_row=T,show_rownames=F,show_colnames=T,fontsize=6)




# Heatmap of new module eigen-genes and sample trait (e.g. Zone)
col_ann <- colData[,c(1,4)]
rownames(col_ann) <- col_ann$Sample
col_ann <- data.frame(col_ann)
col_ann$Stage <- as.factor(col_ann$Stage)
col_ann <- col_ann[order(col_ann$Stage),]
col_ann$sample_ID <- NULL
head(col_ann, 9)
ann_color <- list("col_ann" = c("In Planta" = "purple", 
                                "Early" = "yellow",
                                "Middle" = "green",
                                "Late" = "blue"))

data.me <- data.frame(MEs)
data.me <- data.me[order(match(rownames(data.me), rownames(col_ann))),]
dim(data.me)
[1] 12 17
#pdf(file="newMEs.pdf",heigh=60,width=20)
rownames(MEs)=names(colData[ ,1])
pheatmap(data.me,cluster_col=T,cluster_row=F,show_rownames=F,
         show_colnames=T,fontsize=6,
         annotation_row = col_ann, annotation_colors = ann_color)

Set analysis.

get_module_genes <- function(eigengene_df) {
  module_list <- c("red", "greenyellow", "purple", "lightcyan", "blue", "turquoise",
                   "green", "brown", "yellow", "pink", "tan", "magenta", "midnightblue",
                   "salmon", "black", "cyan")
  gene_list <- list()
  
  for (module in module_list) {
    module_genes <- eigengene_df$gene_id[eigengene_df$V1 > 0.2 & eigengene_df[[paste0("kME", module)]] > 0.7]
    gene_list[[module]] <- module_genes
  }
  
  return(gene_list)
}


module_genes_list_early <- get_module_genes(earlyeiginengene)
module_genes_list_mid <- get_module_genes(mideiginengene)
module_genes_list_late <- get_module_genes(lateeiginengene)
#module_list <- c("red", "greenyellow", "purple", "lightcyan", "blue", "turquoise",
#                 "green", "brown", "yellow", "pink", "tan", "magenta", "midnightblue",
#                 "salmon", "black", "cyan")

#gene_lists <- list(module_genes_list_early$blue, module_genes_list_mid$blue, module_genes_list_late$blue)

#results <- list()
for (i in seq_along(gene_lists)) {
  module.color <- gene_lists[[i]]
  
  ora_res <- as.data.frame(enricher(gene = module.color,
                                    universe = bg,
                                    maxGSSize = 5000,
                                    TERM2GENE = genesets,
                                    pAdjustMethod = "fdr",
                                    pvalueCutoff = 1,
                                    qvalueCutoff = 1))
  
  ora_res$geneID <- NULL
  ora_res <- subset(ora_res, (ora_res$p.adjust < 0.05 & ora_res$Count >= 5))
  ora_res_names <- rownames(ora_res)
  
  ora_res$GeneRatio <- as.character(ora_res$GeneRatio)
  
  gr <- as.numeric(sapply(strsplit(as.character(ora_res$GeneRatio), "/"), "[[", 1)) /
    as.numeric(sapply(strsplit(as.character(ora_res$GeneRatio), "/"), "[[", 2))
  
  br <- as.numeric(sapply(strsplit(as.character(ora_res$BgRatio), "/"), "[[", 1)) /
    as.numeric(sapply(strsplit(as.character(ora_res$BgRatio), "/"), "[[", 2))
  
  ora_res$es <- gr/br
  ora_res <- ora_res[order(-ora_res$es), ]
  ora_res$Description <- NULL
  
  ora_res$Time <- c("early", "mid", "late")[i]
  
  results[[i]] <- ora_res
}
View(ora_res)

Session Information

LS0tCnRpdGxlOiAiV0dDTkEgb2YgUGMgdGltZWNvdXJzZSIKYXV0aG9yOiAiQlNjaHJvZXRlciIKZGF0ZTogIjIwMjMtMDMtMjAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIHByZXR0eWRvYzo6aHRtbF9wcmV0dHk6CiAgICB0aGVtZTogaHBzdHIKICAgIGhpZ2hsaWdodDogZ2l0aHViCiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBsdW1lbgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKIyBQdXJwb3NlCgoxLiBCdWlsZCBhIHdlaWdodGVkIGFuZCBkaXJlY3RlZCBuZXR3b3JrIG9mIGdlbmVzIGJhc2VkIG9uIHRoZSBEdWFsIFJOQSBzZXEgZGF0YSBnZW5lcmF0ZWQgZnJvbSB0aGUgdGltZWNvdXJzZSBvZiBQaHl0b3BodGhvcmEgY2lubmFtb21pIGFuZCBMdXBpbnVzIGFuZ3VzdGlmb2xpdXMgCjIuIERldGVybWluZSBtb2R1bGVzIChjbHVzdGVycyBvZiBnZW5lcykgZnJvbSB0aGUgbmV0d29yayBhc3NvY2lhdGVkIHRvIGRpc2Vhc2UgcHJvZ3Jlc3Npb24gKGZvciBleGFtcGxlLCBiaW90cm9waGljIG9yIG5lY3JvdHJvcGhpYyBzdGFnZXMpIAozLiBFeHRyYWN0IHRoZSBuYW1lcyBvZiB0aGUgZ2VuZXMgZnJvbSB0aGVzZSBodWJzIGFuZCBjb21wYXJlIHRoZW0gdG8gdGhlIGdlbmVzIGluIHRoZSBsaXRlcmF0dXJlIAo0LiBGdW5jdGlvbmFsbHkgYW5ub3RhdGUgdGhlc2UgZ2VuZXMKCldvcmtpbmcgaWRlYTogVGhpcyBuZXR3b3JrIHdpbGwgdGhlbiBiZSB1c2VkIHRvIGlkZW50aWZ5IGh1YiBnZW5lcyBmb3IgZWFjaCBvZiB0aGUgZWFybHksIG1pZGRsZSBhbmQgbGF0ZSBpbnRlcmFjdGlvbnMuCgojIyBMb2FkIExpYnJhcmllcwoKYGBge3IsIGxvYWQgbGlicmFyaWVzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFLCBwYWdlZC5wcmludD1UUlVFfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoe2MoCmxpYnJhcnkoV0dDTkEpLApsaWJyYXJ5KERFU2VxMiksCmxpYnJhcnkoR0VPcXVlcnkpLApsaWJyYXJ5KHRpZHl2ZXJzZSksCmxpYnJhcnkoZ3JpZEV4dHJhKSwKbGlicmFyeShyZXNoYXBlMiksCmxpYnJhcnkocnRyYWNrbGF5ZXIpLApsaWJyYXJ5KGRwbHlyKSwKbGlicmFyeShlZGdlUikpfSkKYGBgCgpMb2FkIFBoeXRvcGh0aG9yYSBjaW5uYW1vbWkgZ3RmIGZpbGUsIGNsZWFuIHVwIHRvIGdldCBHZW5lIElEcwoKQ29tYmluZSBhbHRlcm5hdGl2ZWx5IHNwbGljZWQgdHJhbnNjcmlwdHMgKHNlZW4gaW4gdHJhbnNjcmlwdF9pZCBhcyAtUkEgYW5kIFJCKQoKYGBge3J9CnBjLmd0ZiA8LSBydHJhY2tsYXllcjo6aW1wb3J0KCJodHRwczovL2Z0cC5uY2JpLm5sbS5uaWguZ292L2dlbm9tZXMvZ2VuYmFuay9wcm90b3pvYS9QaHl0b3BodGhvcmFfY2lubmFtb21pL2xhdGVzdF9hc3NlbWJseV92ZXJzaW9ucy9HQ0FfMDE4NjkxNzE1LjFfQVNNMTg2OTE3MXYxL0dDQV8wMTg2OTE3MTUuMV9BU00xODY5MTcxdjFfZ2Vub21pYy5ndGYuZ3oiKQpwYy5kZiA8LSBhcy5kYXRhLmZyYW1lKChwYy5ndGYpKQoKcGMuZ2VuZXMgPC0gZmlsdGVyKHBjLmRmLCB0eXBlID09ICJzdGFydF9jb2RvbiIpCnBjLmdlbmVpZHMgPC0gc2VsZWN0KHBjLmdlbmVzLCBnZW5lX2lkLCBwcm9kdWN0LCBwcm90ZWluX2lkKSAgCgpwYy5hbm5vdGF0aW9ucyA8LSBwYy5nZW5laWRzICU+JSAKICBkaXN0aW5jdChnZW5lX2lkLCAua2VlcF9hbGwgPSBUKQpucm93KHBjLmFubm90YXRpb25zKQoKcGNhbm5vdCA8LSBhcy5tYXRyaXgocGMuYW5ub3RhdGlvbnMpCgojd3JpdGUudGFibGUocGNhbm5vdCwgZmlsZSA9ICJwY2dlbmVpZHMudHN2IixxdW90ZT1GQUxTRSxzZXA9Ilx0IikKYGBgCgpMb2FkIEdPIHRlcm1zIGJsYXN0ZWQgZnJvbSBPbWljc2JveCAKYGBge3J9CmdvdGVybXMgPC0gcmVhZC5kZWxpbSgncGMuZ290ZXJtcy50eHQnLCBoZWFkZXIgPSBUUlVFKSAlPiUKICBzZWxlY3QoYyhTZXFOYW1lLCBHTy5JRHMsIEdPLk5hbWVzLCBEZXNjcmlwdGlvbikpCiAgCmhlYWQoZ290ZXJtcykKCmNvbG5hbWVzKGdvdGVybXMpIDwtIGMoInByb3RlaW5faWQiLCAiR09fdGVybXMiLCAiR09fZGVzY3JpcHRvbiIsICJkZXNjcmlwdGlvbiIpCgpnb3Rlcm1zLmRmIDwtIGRwbHlyOjpyaWdodF9qb2luKGdvdGVybXMsIHBjLmFubm90YXRpb25zLCBieSA9ICdwcm90ZWluX2lkJykKbnJvdyhnb3Rlcm1zLmRmKQpgYGAKCk1ha2UgR08gdGVybSBsaXN0IGZvciBlbnJpY2htZW50IGFuYWx5c2lzCgpgYGB7cn0Kc3BsaXRndC5kZiA8LSBhcy50aWJibGUoZ290ZXJtcy5kZikgJT4lCiAgc2VwYXJhdGVfbG9uZ2VyX2RlbGltKGMoR09fdGVybXMsIEdPX2Rlc2NyaXB0b24pLCBkZWxpbSA9ICI7IikKCnNwbGl0Z3QuZGYgPC0gYXMuZGF0YS5mcmFtZShzcGxpdGd0LmRmKQoKI3N0cmlwIGxlYWRpbmcgc3BhY2VzCnNwbGl0Z3QuZGYkR09fdGVybXMgPC0gZ3N1YigiICIsIiIsc3BsaXRndC5kZiRHT190ZXJtcykKc3BsaXRndC5kZiRHT19kZXNjcmlwdG9uIDwtIGdzdWIoIl4gIiwiIixzcGxpdGd0LmRmJEdPX2Rlc2NyaXB0b24pCmhlYWQoc3BsaXRndC5kZiwgMTApCgpndSA8LSB1bmlxdWUoc3BsaXRndC5kZiRHT190ZXJtcykKaGVhZChndSwyMCkKCmd1bCA8LSBsYXBwbHkoMTpsZW5ndGgoZ3UpLCBmdW5jdGlvbihpKXsKICBteWdvIDwtIGd1W2ldCiAgdW5pcXVlKHNwbGl0Z3QuZGZbc3BsaXRndC5kZiRHT190ZXJtcyA9PSBteWdvLCAiZ2VuZV9pZCJdKQp9KQoKbmFtZXMoZ3VsKSA8LSBsYXBwbHkoMTpsZW5ndGgoZ3UpLCBmdW5jdGlvbihpKXsKICBteWdvIDwtIGd1W2ldCiAgZGVzYyA8LSBoZWFkKHNwbGl0Z3QuZGZbc3BsaXRndC5kZiRHT190ZXJtcyA9PSBteWdvLCAiR09fZGVzY3JpcHRvbiJdLDEpCiAgaWRfZGVzYyA8LSBwYXN0ZShteWdvLGRlc2MpCn0pCgpoZWFkKG5hbWVzKGd1bCkpCgpgYGAKCkFzc2VtYmxlIGNvdW50IG1hdHJpeCBhbmQgY29sZGF0YSBmaWxlCgpgYGB7ciwgZWNobz1GQUxTRX0KdG1wPC1yZWFkLnRhYmxlKCIzY29sLnRzdi5neiIsaGVhZGVyPUYpCng8LWFzLm1hdHJpeChhY2FzdCh0bXAsIFYyflYxLCB2YWx1ZS52YXI9IlYzIikpCmNvbG5hbWVzKHgpPC1zYXBwbHkoc3Ryc3BsaXQoY29sbmFtZXMoeCksIl8iKSwiW1siLDEpCgojaGVhZCh4KQojd3JpdGUudGFibGUoeCxmaWxlPSJjb3VudG1hdHJpeC50c3YiLHF1b3RlPUZBTFNFLHNlcD0iXHQiKQpgYGAKCkltcG9ydCBmaWxlcyBpbnRvIHIKCmBgYHtyfQpjb2xkYXRhIDwtIHJlYWQudGFibGUoJ3NhbXBsZV9pbmZvLnRzdicpClBjZ2VuZW5hbWVzIDwtIHJlYWQudGFibGUoJ3BjZ2VuZWlkcy50c3YnLCByb3cubmFtZXMgPSAxLCBxdW90ZSA9ICIiLCBzZXA9J1x0JywgZmlsbCA9IFRSVUUsIGhlYWRlciA9IFRSVUUpCklVTTgzVGFuamlsY291bnRtYXRyaXggPC0gcmVhZC50YWJsZSgnY291bnRtYXRyaXgudHN2JykgCmBgYAoKQ2xlYW4gY291bnRtYXRyaXggZm9yIFAuIGNpbm5hbW9taSBhbmFseXNpcwoKYGBge3J9CmNvbGxhYmVscyA8LSBjb2xuYW1lcyhJVU04M1RhbmppbGNvdW50bWF0cml4KSA8LSAoY29sZGF0YSRTYW1wbGUpCmNvbG5hbWVzKElVTTgzVGFuamlsY291bnRtYXRyaXgpIDwtIGNvbGxhYmVscwoKSVVNODNUYW5qaWxjb3VudG1hdHJpeCA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihJVU04M1RhbmppbGNvdW50bWF0cml4LCAiZ2VuZV9pZCIpCgpJVU04M0NvdW50TWF0cml4IDwtIElVTTgzVGFuamlsY291bnRtYXRyaXggJT4lIAogIGZpbHRlcihzdHJfZGV0ZWN0KGdlbmVfaWQsICJJVU04MyIpKSAKICAKcm93bmFtZXMoSVVNODNDb3VudE1hdHJpeCkgPC0gSVVNODNDb3VudE1hdHJpeCRnZW5lX2lkCklVTTgzQ291bnRNYXRyaXggPC0gSVVNODNDb3VudE1hdHJpeCBbICwtMV0KSVVNODNDb3VudE1hdHJpeCA8LSBhcy5kYXRhLmZyYW1lKElVTTgzQ291bnRNYXRyaXgpCklVTTgzQ291bnRNYXRyaXggPC0gSVVNODNDb3VudE1hdHJpeFsgLC0oMToxMildCgojd3JpdGUudGFibGUoSVVNODNDb3VudE1hdHJpeCwgZmlsZSA9ICJQY19jb3VudG1hdHJpeC50c3YiLHF1b3RlPUZBTFNFLHNlcD0iXHQiKQpgYGAKCkNsZWFuIGNvbGRhdGEgZm9yIERlc2VxMiBub3JtYWxpc2F0aW9uCgpgYGB7cn0KY29sRGF0YSA8LSBjb2xkYXRhICU+JSAKICBmaWx0ZXIoc3RyX2RldGVjdChTYW1wbGUsICJQYyIpKQoKcm93bmFtZXMoY29sRGF0YSkgPC0gY29sRGF0YSRTYW1wbGUKY29sRGF0YSA8LSBjb2xEYXRhIFsgLC0xXSAKYGBgCgpEZXRlY3QgYW5kIHJlbW92ZSBvdXRsaWVyIGdlbmVzCgpgYGB7cn0KI0NhbGwgYSBmdW5jdGlvbiBmcm9tIFdHQ05BIHBhY2thZ2UgdGhhdCBkZXRlY3RzIG91dGxpZXJzIAojUm93cyBzaG91bGQgYmUgdGhlIHNhbXBsZXMgYW5kIHRoZSBjb2x1bW5zIGdlbmVzCmdzZyA8LSBnb29kU2FtcGxlc0dlbmVzKHQoSVVNODNDb3VudE1hdHJpeCkpCnN1bW1hcnkoZ3NnKQoKI0lmIGZhbHNlLCB0aGVuIHRoZXJlIGFyZSBvdXRsaWVycwpnc2ckYWxsT0sgI0ZhbHNlCgp0YWJsZShnc2ckZ29vZEdlbmVzKSAjMzAxMnRvIGJlIGV4Y2x1ZGVkCnRhYmxlKGdzZyRnb29kU2FtcGxlcykgI0FsbCAxMiBzYW1wbGVzIHBhc3NlZAoKIyByZW1vdmUgZ2VuZXMgdGhhdCBhcmUgZGV0ZWN0ZCBhcyBvdXRsaWVycwpkYXRhIDwtIElVTTgzQ291bnRNYXRyaXhbZ3NnJGdvb2RHZW5lcyA9PSBUUlVFLF0KYGBgCgpEZXRlY3Qgb3V0bGllciBzYW1wbGVzCgpgYGB7cn0KIyBkZXRlY3Qgb3V0bGllciBzYW1wbGVzIC0gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKaHRyZWUgPC0gaGNsdXN0KGRpc3QodChkYXRhKSksIG1ldGhvZCA9ICJhdmVyYWdlIikKZ3JvdXBzIDwtIGN1dHJlZShodHJlZSwgaz0yKSAjIGN1dCB0cmVlIGludG8gY2x1c3RlcnMKCnBsb3QoaHRyZWUsIGxhYmVscyhncm91cHMpKQojIGRyYXcgZGVuZG9ncmFtIHdpdGggcmVkIGJvcmRlcnMgYXJvdW5kIHRoZSBjbHVzdGVycwpyZWN0LmhjbHVzdChodHJlZSwgaz0yLCBib3JkZXI9InJlZCIpCmBgYAojIE5vcm1hbGlzYXRpb24gdXNpbmcgREVTZXEyIHBhY2thZ2UKClRoZSBXR0NOQSBsaWJyYXJ5IHJlcXVpcmVzIG5vcm1hbGlzYXRpb24gdXNpbmcgdGhlIHZzdCAodmFyaWFuY2Utc3RhYmlsaXNpbmcgdHJhbnNmb3JtKSBmdW5jdGlvbiBmcm9tIHRoZSBERVNlcTIgcGFja2FnZQoKYGBge3J9CiMgZXhjbHVkZSBvdXRsaWVyIHNhbXBsZXMgZnJvbSB0aGUgY29sdW1uIGRhdGEgdG8gbWF0Y2ggdGhlIG5ldyBkYXRhCiNjb2xEYXRhIDwtIGNvbERhdGEgJT4lIAojICBmaWx0ZXIoIXJvdy5uYW1lcyguKSAlaW4lIG91dGxpZXJzKQoKIyBtYWtpbmcgc3VyZSB0aGUgcm93bmFtZXMgYW5kIGNvbHVtbiBuYW1lcyBpZGVudGljYWwgZm9yIHRoZSB0d28gZGF0YSBzZXRzCiNhbGwocm93bmFtZXMoY29sRGF0YSkgPT0gY29sbmFtZXMoY2x1c3RlcnMpKQoKIyBjcmVhdGUgREVTZXEgRGF0YSBTZXQKZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gMSkgIyBubyBtb2RlbCwgZm9yIG5vcm1hbGlzYXRpb24gb25seQoKIyByZW1vdmUgYWxsIGdlbmVzIHdpdGggY291bnRzIDwgMTAgaW4gbW9yZSB0aGFuIDkwJSBvZiBzYW1wbGVzICgxMiowLjk9MTAuOCB+IDExKSBhcyBzdWdnZXN0ZWQgYnkgV0dDTkEgb24gUk5Bc2VxIEZBUSAoaHR0cHM6Ly9ob3J2YXRoLmdlbmV0aWNzLnVjbGEuZWR1L2h0bWwvQ29leHByZXNzaW9uTmV0d29yay9ScGFja2FnZXMvV0dDTkEvZmFxLmh0bWwpCiMgPDEwIGluIG1vcmUgdGhhbiA5MCUgc2FtcGxlcwpkZHM5MCA8LSBkZHNbcm93U3Vtcyhjb3VudHMoZGRzKSA+PSAxMCkgPj0gMTEsXQpucm93KGRkczkwKSAjIDU0MDMgZ2VuZXMKCgojID49IDEwKSA+PSAxMSxdIDU0MDMgZ2VuZXMgPD09PT09ICNsZXNzIHRoYW4gMTAgY291bnRzIGluICU5MCBvZiBzYW1wbGVzCiMgPj0gMTUpID49IDExLF0gNDQyMCBnZW5lcyAKCmdlbmUubGlzdCA8LSByb3cubmFtZXMoZGRzOTApCiNsYXBwbHkoZ2VuZS5saXN0LCB3cml0ZSwgImFsbF9nZW5lcy50eHQiLCBhcHBlbmQ9VFJVRSkKCiMgcGVyZm9ybSB2YXJpYW5jZSBzdGFiaWxpemF0aW9uCmRkc19ub3JtIDwtIHZzdChkZHM5MCkKCiMgZ2V0IG5vcm1hbGl6ZWQgY291bnRzCm5vcm0uY291bnRzIDwtIGFzc2F5KGRkc19ub3JtKSAlPiUgCiAgdCgpCgp5b3VyX2RkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkczkwKQp5b3VyX2RkcyA8LSBlc3RpbWF0ZURpc3BlcnNpb25zKHlvdXJfZGRzKQoKIyBQbG90IGRpc3BlcnNpb24gZXN0aW1hdGVzIGFuZCBmaXRzCnBsb3REaXNwRXN0cyh5b3VyX2RkcywgbWFpbiA9ICJEaXNwZXJzaW9uIFRyZW5kIHdpdGggTG9jYWwgYW5kIFBhcmFtZXRyaWMgRml0cyIpCgoKI1NhdmUgbm9ybWFsaXNlZCBkYXRhc2V0IGZvciBmYXN0ZXIgYW5hbHlzaXMgbmV4dCB0aW1lCiNzYXZlUkRTKG5vcm0uY291bnRzLCAibm9ybS5jb3VudHMucmRzIikKYGBgCgojIE5ldHdvcmsgQ29uc3RydWN0aW9uCgpgYGB7cn0KIyBsb2FkIG5vcm0uY291bnRzIG9iamVjdAojbm9ybS5jb3VudHMgPC0gcmVhZFJEUygibm9ybS5jb3VudHMiKQoKIyBDaG9vc2UgYSBzZXQgb2Ygc29mdC10aHJlc2hvbGRpbmcgcG93ZXJzIHRvIGNyZWF0ZSBhIHNjYWxlLWZyZWUgbmV0d29yawpwb3dlciA8LSBjKGMoMToxMCksIHNlcShmcm9tID0gMTIsIHRvID0gMzAsIGJ5ID0gMikpCgojIENhbGwgdGhlIG5ldHdvcmsgdG9wb2xvZ3kgYW5hbHlzaXMgZnVuY3Rpb24Kc2Z0IDwtIHBpY2tTb2Z0VGhyZXNob2xkKG5vcm0uY291bnRzLAogICAgICAgICAgICAgICAgICBwb3dlclZlY3RvciA9IHBvd2VyLAogICAgICAgICAgICAgICAgICBuZXR3b3JrVHlwZSA9ICJzaWduZWQiLAogICAgICAgICAgICAgICAgICBSc3F1YXJlZEN1dCA9IDAuOTAsCiAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSA1KQoKc2Z0LmRhdGEgPC0gc2Z0JGZpdEluZGljZXMKCmExIDwtIGdncGxvdChzZnQuZGF0YSwgYWVzKFBvd2VyLCBTRlQuUi5zcSwgbGFiZWwgPSBQb3dlcikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dChudWRnZV95ID0gMC4xKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMC45MCwgY29sb3IgPSAncmVkJykgKwogIGxhYnMoeCA9ICdQb3dlcicsCiAgICAgICB5ID0gJ1NjYWxlIGZyZWUgdG9wb2xvZ3kgbW9kZWwgZml0LCBzaWduZWQgUl4yJywKICAgICAgIHRpdGxlID0gJ1NjYWxlIGluZGVwZW5kZW5jZScpICsKICB0aGVtZV9jbGFzc2ljKCkKCmEyIDwtIGdncGxvdChzZnQuZGF0YSwgYWVzKFBvd2VyLCBtZWFuLmsuLCBsYWJlbCA9IFBvd2VyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KG51ZGdlX3kgPSAwLjEpICsKICBsYWJzKHggPSAnUG93ZXInLAogICAgICAgeSA9ICdNZWFuIENvbm5lY3Rpdml0eScsCiAgICAgICB0aXRsZSA9ICdNZWFuIGNvbm5lY3Rpdml0eScpICsKICB0aGVtZV9jbGFzc2ljKCkKICAKZ3JpZC5hcnJhbmdlKGExLCBhMiwgbnJvdyA9IDIpIAoKc29mdF9wb3dlciA8LSBzZnQkcG93ZXJFc3RpbWF0ZSAjc29mdF9wb3dlciA8LSAxOAoKYGBgCgpDcmVhdGUgbmV0d29yayBhbmQgaWRlbnRpZnkgbW9kdWxlcwoKTm90ZTogdGhpcyBjaHVuayBpcyBzZXQgbm90IHRvIHJ1biBzaW5jZSB0aGUgYmxvY2t3aXNlIE1vZHVsZXMgZnVuY3Rpb24gdGFrZXMgYSBsb25nIHRpbWUgdG8gcnVuLiBUaGUgUiBvYmplY3QgaGFzIGJlZW4gc2F2ZWQgYW5kIGlzIGxvYWRlZCBpbiB0aGUgbmV4dCBjaHVuayBmb3IgYSBmYXN0ZXIgcnVuIHRpbWUuCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIGNvbnZlcnQgbWF0cml4IHRvIG51bWVyaWMKbm9ybS5jb3VudHNbXSA8LSBzYXBwbHkobm9ybS5jb3VudHMsIGFzLm51bWVyaWMpCgojU3VjY2Vzc2l2ZWx5LCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyB3YXMgcGVyZm9ybWVkIHRvIGlkZW50aWZ5IG1vZHVsZXMKdGVtcF9jb3IgPC0gY29yCmNvciA8LSBXR0NOQTo6Y29yCgojIFBhcmFtZXRlcnMgdG8gYmUgdHdlYWtlZCBsYXRlcgpid25ldCA8LSBibG9ja3dpc2VNb2R1bGVzKG5vcm0uY291bnRzLAogICAgICAgICAgICAgICAgIG1heEJsb2NrU2l6ZSA9IDc1MDAsICMgc2VsZWN0ZWQgdG90YWwgbnVtYmVyIG9mIGdlbmVzIHNpbmNlIENQVSBtZW1vcnkgaXMgc3VmZmljaWVudCAoMzJHQiB3b3Jrc3RhdGlvbiBzaG91bGQgYmUgYWJsZSB0byBoYW5kbGUgcGVyaGFwcyAzMDAwMCkKICAgICAgICAgICAgICAgICBUT01UeXBlID0gInNpZ25lZCIsCiAgICAgICAgICAgICAgICAgcG93ZXIgPSBzb2Z0X3Bvd2VyLAogICAgICAgICAgICAgICAgIG1lcmdlQ3V0SGVpZ2h0ID0gMC4xMCwKICAgICAgICAgICAgICAgICBzYXZlVE9NcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgc2F2ZVRPTUZpbGVCYXNlID0gInBjX1RPTSIsCiAgICAgICAgICAgICAgICAgbnVtZXJpY0xhYmVscyA9IEZBTFNFLCAjIHNldCBhcyBmYWxzZSB0byBhc3NpZ24gY29sb3IgYXMgbGFiZWxzCiAgICAgICAgICAgICAgICAgcmFuZG9tU2VlZCA9IDIxNDMsICMgZm9yIHJlcHJvZHVjaWJpbGl0eSBzaW5jZSB0aGlzIGZ1bmN0aW9uIHVzZXMgY2x1c3RlcmluZwogICAgICAgICAgICAgICAgIHZlcmJvc2UgPSAzKQoKIyBTYXZlIGJ3bmV0IG9iamVjdApzYXZlUkRTKGJ3bmV0LCAiYnduZXQucmRzIikKCmNvciA8LSB0ZW1wX2NvcgpgYGAKCgoKCmBgYHtyfQojIGxvYWQgYnduZXQgb2JqZWN0CmJ3bmV0IDwtIHJlYWRSRFMoImJ3bmV0LnJkcyIpCgptb2R1bGVfZWlnZW5nZW5lcyA8LSBid25ldCRNRXMgJT4lCiAgb3JkZXJNRXMoLikKCmhlYWQobW9kdWxlX2VpZ2VuZ2VuZXMpCgoKIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCB0aGUgbW9kdWxlIGNvbG9ycyBiZWZvcmUgYW5kIGFmdGVyIG1lcmdpbmcgdW5kZXJuZWF0aApwbG90RGVuZHJvQW5kQ29sb3JzKGJ3bmV0JGRlbmRyb2dyYW1zW1sxXV0sIGNiaW5kKGJ3bmV0JHVubWVyZ2VkQ29sb3JzLCBid25ldCRjb2xvcnMpLAogICAgICAgICAgICAgICAgICAgIGMoInVubWVyZ2VkIiwgIm1lcmdlZCIpLAogICAgICAgICAgICAgICAgICAgIGRlbmRyb0xhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgIGFkZEd1aWRlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICBoYW5nPSAwLjAzLAogICAgICAgICAgICAgICAgICAgIGd1aWRlSGFuZyA9IDAuMDUpCgogIyBncmV5IG1vZHVsZSA9IGFsbCBnZW5lcyB0aGF0IGRvZXNuJ3QgZmFsbCBpbnRvIG90aGVyIG1vZHVsZXMKIAptb2R1bGUudG90YWwgPC0gdGFibGUoYnduZXQkY29sb3JzKQpgYGAKCiMgUmVsYXRlIHRoZSBtb2R1bGVzIHRvIHN0YWdlcwoKYGBge3J9CiNjcmVhdGUgdHJhaXRzIGZpbGUgLSBhc3NpZ24gMSBpZiBhIHNhbXBsZSBpcyBhIGNlcnRhaW4gc3RhZ2UsIGVsc2UgYXNzaWduIDAKRGlzX3RyYWl0cyA8LSBjb2xEYXRhICU+JSAKICBtdXRhdGUoRGlzLnZzLmFsbCA9IGlmZWxzZShncmVwbCgnVHJlYXRlZCcsIFRyZWF0bWVudCksIDEsIDApKSAlPiUgCiAgc2VsZWN0KDUpCgoKZmFjdG9yX2xldmVscyA8LSB1bmlxdWUoY29sRGF0YSRTdGFnZSkKCiMgdHJhbnNmb3JtIHN0YWdlcyBpbnRvIGZhY3RvcnMgYW5kIGRlZmluZSBsZXZlbHMKY29sRGF0YSRTdGFnZSA8LSBmYWN0b3IoY29sRGF0YSRTdGFnZSwgbGV2ZWxzID0gZmFjdG9yX2xldmVscykKCnRyYWl0cyA8LSBiaW5hcml6ZUNhdGVnb3JpY2FsQ29sdW1ucyhjb2xEYXRhJFN0YWdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAjZHJvcEZpcnN0TGV2ZWxWc0FsbCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlUGFpcndpc2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZUxldmVsVnNBbGwgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5Db3VudCA9IDEpCgp0cmFpdHMgPC0gY2JpbmQoRGlzX3RyYWl0cywgdHJhaXRzKQpgYGAKCmBgYHtyIHIsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEyfQojIERlZmluZSBudW1iZXJzIG9mIGdlbmVzIGFuZCBzYW1wbGVzCm5TYW1wbGVzIDwtIG5yb3cobm9ybS5jb3VudHMpCm5HZW5lcyA8LSBuY29sKG5vcm0uY291bnRzKQoKIyBjb3JyZWxhdGlvbiBmb3IgbW9kdWxlIGVpZ2VuZ2VuZXMgYW5kIHRyYWl0cwptb2R1bGUudHJhaXQuY29yciA8LSBjb3IobW9kdWxlX2VpZ2VuZ2VuZXMsIHRyYWl0cywgdXNlID0gJ3AnKQptb2R1bGUudHJhaXQuY29yci5wdmFscyA8LSBjb3JQdmFsdWVTdHVkZW50KG1vZHVsZS50cmFpdC5jb3JyLCBuU2FtcGxlcykKCiMgSGVhdCBtYXAgdjIgKGZyb20gV0dDTkEgdHV0b3JpYWwpCnRleHRNYXRyaXggPSAgcGFzdGUoc2lnbmlmKG1vZHVsZS50cmFpdC5jb3JyLCAyKSwgIlxuKCIsCiAgICAgICAgICAgICAgICAgICAgc2lnbmlmKG1vZHVsZS50cmFpdC5jb3JyLnB2YWxzLCAxKSwgIikiLCBzZXAgPSAiIikKZGltKHRleHRNYXRyaXgpID0gZGltKG1vZHVsZS50cmFpdC5jb3JyKQoKCgpwYXIobWFyID0gYyg2LCA4LjUsIDMsIDMpKTsKbGFiZWxlZEhlYXRtYXAoTWF0cml4ID0gbW9kdWxlLnRyYWl0LmNvcnIsCiAgICAgICAgICAgICAgIHhMYWJlbHMgPSBjb2xuYW1lcyhtb2R1bGUudHJhaXQuY29yciksCiAgICAgICAgICAgICAgIHlMYWJlbHMgPSByb3duYW1lcyhtb2R1bGUudHJhaXQuY29yciksCiAgICAgICAgICAgICAgIHlTeW1ib2xzID0gcm93bmFtZXMobW9kdWxlLnRyYWl0LmNvcnIpLAogICAgICAgICAgICAgICBjb2xvckxhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICBjb2xvcnMgPSBibHVlV2hpdGVSZWQoNTApLAogICAgICAgICAgICAgICB0ZXh0TWF0cml4ID0gdGV4dE1hdHJpeCwKICAgICAgICAgICAgICAgdGV4dEFkaiA9IGMoMC41LCAwLjUpLAogICAgICAgICAgICAgICBzZXRTdGRNYXJnaW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgIGNleC5sYWIueSA9IDAuNiwKICAgICAgICAgICAgICAgY2V4LnRleHQgPSAwLjcsCiAgICAgICAgICAgICAgIHpsaW0gPSBjKC0xLDEpLAogICAgICAgICAgICAgICBtYWluID0gcGFzdGUoIk1vZHVsZS10cmFpdCByZWxhdGlvbnNoaXBzIikpCgoKCiMgZ2V0IG51bWJlciBvZiBnZW5lcyBmb3IgZWFjaCBtb2R1bGUKdGFibGUoYnduZXQkY29sb3JzKQoKI1RhZyBnZW5lcyB3aXRoIG1vZHVsZSBtZW1iZXJzaGlwIGFuZCBzdG9yZSBpdCBpbiBhIHRhYmxlCm1vZHVsZS5nZW5lLm1hcHBpbmcgPC0gYXMuZGF0YS5mcmFtZShid25ldCRjb2xvcnMpCm1vZHVsZS5nZW5lLm1hcHBpbmcKCmBgYApTaWduaWZpY2FuY2UgaW4gYnJhY2tldHMgc2hvd3MgaG93IHNpZ25pZmljYW50bHkgYXNzb2NpYXRlZCB0aGUgbW9kdWxlIChjbHVzdGVyIG9mIGdlbmVzKSBpcyB0aGUgdHJhaXQgb2YgaW50ZXJlc3QgIApGaW5kIG1vZHVsZXMgdGhhdCBoYXZlIHNpZ25pZmljYW50IGFzc29jaWF0aW9uIHdpdGggZGlzZWFzZSBzdGF0ZQoKQ2FsY3VsYXRlIHRoZSBtb2R1bGUgbWVtYmVyc2hpcCBhbmQgdGhlIGFzc29jaWF0ZWQgcC12YWx1ZXMKVGhlIG1vZHVsZSBtZW1iZXJzaGlwL2ludHJhbW9kdWxhciBjb25uZWN0aXZpdHkgaXMgY2FsY3VsYXRlZCBhcyB0aGUgY29ycmVsYXRpb24gb2YgdGhlIGVpZ2VuZ2VuZSBhbmQgdGhlIGdlbmUgZXhwcmVzc2lvbiBwcm9maWxlLgpUaGlzIHF1YW50aWZpZXMgdGhlIHNpbWlsYXJpdHkgb2YgYWxsIGdlbmVzIG9uIHRoZSBhcnJheSB0byBldmVyeSBtb2R1bGUuCgpVc2luZyB0aGUgZ2VuZSBzaWduaWZpY2FuY2UgeW91IGNhbiBpZGVudGlmeSBnZW5lcyB0aGF0IGhhdmUgYSBoaWdoIHNpZ25pZmljYW5jZSBmb3IgdHJhaXQgb2YgaW50ZXJlc3QgClVzaW5nIHRoZSBtb2R1bGUgbWVtYmVyc2hpcCBtZWFzdXJlcyB5b3UgY2FuIGlkZW50aWZ5IGdlbmVzIHdpdGggaGlnaCBtb2R1bGUgbWVtYmVyc2hpcCBpbiBpbnRlcmVzdGluZyBtb2R1bGVzLgoKIyBHZW5lIFNpZ25pZmljYW5jZSBhdCB0aGUgRWFybHkgUGMtbHVwaW4gaW50ZXJhY3Rpb24KCmBgYHtyfQojIERlZmluZSBhIGdlbmUgc2lnbmlmaWNhbmNlIHZhcmlhYmxlIGZvciBFYXJseQpHUy5FYXJseSA8LSBhcy5udW1lcmljKGNvcihub3JtLmNvdW50cywgdHJhaXRzJGRhdGEuRWFybHkudnMuYWxsLCB1c2UgPSAicCIpKQoKIyBUaGlzIHRyYW5zbGF0ZXMgdGhlIG51bWVyaWMgdmFsdWVzIGludG8gY29sb3JzCkdTLnN0YWdlX2Vhcmx5Q29sb3IgPSBudW1iZXJzMmNvbG9ycyhHUy5FYXJseSwgc2lnbmVkID0gVCkKYmxvY2tudW1iZXIgPSAxCgptb2R1bGVMYWJlbHNBdXRvbWF0aWMgPSBid25ldCRjb2xvcnMKIyBDb252ZXJ0IGxhYmVscyB0byBjb2xvcnMgZm9yIHBsb3R0aW5nCm1vZHVsZUNvbG9yc0F1dG9tYXRpYyA9IGxhYmVsczJjb2xvcnMobW9kdWxlTGFiZWxzQXV0b21hdGljKQoKZGF0Q29sb3JzID0gZGF0YS5mcmFtZShid25ldCRjb2xvcnMsIEdTLnN0YWdlX2Vhcmx5Q29sb3IpW2J3bmV0JGJsb2NrR2VuZXNbW2Jsb2NrbnVtYmVyXV0sIAogICAgXQoKIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCB0aGUgbW9kdWxlIGNvbG9ycyB1bmRlcm5lYXRoCnBsb3REZW5kcm9BbmRDb2xvcnMoYnduZXQkZGVuZHJvZ3JhbXNbW2Jsb2NrbnVtYmVyXV0sIGNvbG9ycyA9IGRhdENvbG9ycywgZ3JvdXBMYWJlbHMgPSBjKCJNb2R1bGUgY29sb3JzIiwgCiAgICAiR1Muc3RhZ2UuRWFybHkiKSwgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLCBhZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUpCmBgYAoKYGBge3J9CmRhdEtNRSA8LSBzaWduZWRLTUUobm9ybS5jb3VudHMsIG1vZHVsZV9laWdlbmdlbmVzKQoKIyBNZWFzdXJlIHRoZSBjb3JyZWxhdGlvbiBvZiB0aGUgZ2VuZSdzIG1vZHVsZSBtZW1iZXJzaGlwIGFuZCB0aGUgYXNzb2NpYXRlZCBwLXZhbHVlcwptb2R1bGUubWVtYmVyc2hpcC5tZWFzdXJlIDwtIGNvcihtb2R1bGVfZWlnZW5nZW5lcywgbm9ybS5jb3VudHMsIHVzZSA9ICdwJykKbW9kdWxlLm1lbWJlcnNoaXAubWVhc3VyZS5wdmFscyA8LSBjb3JQdmFsdWVTdHVkZW50KG1vZHVsZS5tZW1iZXJzaGlwLm1lYXN1cmUsIG5TYW1wbGVzKQoKIyBDYWxjdWxhdGUgdGhlIGdlbmUgc2lnbmlmaWNhbmNlIGFuZCBhc3NvY2lhdGVkIHAtdmFsdWVzCmVhcmx5LmdlbmUuc2lnbmYuY29yciA8LSBjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLkVhcmx5LnZzLmFsbCwgdXNlID0gJ3AnKQplYXJseS5nZW5lLnNpZ25mLmNvcnIucHZhbHMgPC0gY29yUHZhbHVlU3R1ZGVudChlYXJseS5nZW5lLnNpZ25mLmNvcnIsIG5TYW1wbGVzKQoKCmNvbG9yT2ZDb2x1bW4gPSBzdWJzdHJpbmcobmFtZXMoZGF0S01FKSwgNCkKI3NlbGVjdE1vZHVsZXMgPSBjKCJicm93biIsICJtYWdlbnRhIiwgImJsdWUiLCAidHVycXVvaXNlIiwgImxpZ2h0Y3lhbiIpCnNlbGVjdE1vZHVsZXMgPSBjb2xvck9mQ29sdW1uW2NvbG9yT2ZDb2x1bW4gIT0iZ3JleSJdCgojcGFyKG1mcm93ID0gYyg0LCBsZW5ndGgoc2VsZWN0TW9kdWxlcykvNCkpCiAgZm9yIChtb2R1bGUgaW4gc2VsZWN0TW9kdWxlcykgewogICAgICBjb2x1bW4gPSBtYXRjaChtb2R1bGUsIGNvbG9yT2ZDb2x1bW4pCiAgICAgIHJlc3RNb2R1bGUgPSBtb2R1bGVDb2xvcnNBdXRvbWF0aWMgPT0gbW9kdWxlCiAgICAgIHZlcmJvc2VTY2F0dGVycGxvdChkYXRLTUVbcmVzdE1vZHVsZSwgY29sdW1uXSwgR1MuRWFybHlbcmVzdE1vZHVsZV0sIHhsYWIgPSBwYXN0ZSgiTW9kdWxlIE1lbWJlcnNoaXAiLCAKICAgICAgICAgIG1vZHVsZSwgIm1vZHVsZSIpLCB5bGFiID0gIkdTLnN0YWdlX0Vhcmx5IiwgbWFpbiA9IHBhc3RlKCJrTUUuIiwgbW9kdWxlLCAKICAgICAgICAgICJ2cy4gR1MiKSwgY29sID0gbW9kdWxlKQogIH0KYGBgCgpgYGB7ciwgRWFybHllZ2luZW5nZW5lc30KZWFybHlncyA8LSBlYXJseS5nZW5lLnNpZ25mLmNvcnIgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9pZCIpIAoKZWFybHlnc2lkIDwtIGRwbHlyOjpyaWdodF9qb2luKHBjLmFubm90YXRpb25zLCBlYXJseWdzLCBieSA9ICdnZW5lX2lkJykKCmRhdEttZS5pZCA8LSBhcy5kYXRhLmZyYW1lKGRhdEtNRSkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX2lkIikgCgplYXJseWVpZ2luZW5nZW5lIDwtIGRwbHlyOjpyaWdodF9qb2luKGVhcmx5Z3NpZCwgZGF0S21lLmlkLCBieSA9ICdnZW5lX2lkJykKYGBgCgojIEdlbmUgU2lnbmlmaWNhbmNlIGF0IHRoZSBNaWQgUGMtbHVwaW4gaW50ZXJhY3Rpb24KCmBgYHtyfQojIERlZmluZSBhIGdlbmUgc2lnbmlmaWNhbmNlIHZhcmlhYmxlIGZvciBFYXJseQpHUy5taWQgPC0gYXMubnVtZXJpYyhjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLk1pZGRsZS52cy5hbGwsIHVzZSA9ICJwIikpCgojIFRoaXMgdHJhbnNsYXRlcyB0aGUgbnVtZXJpYyB2YWx1ZXMgaW50byBjb2xvcnMKR1Muc3RhZ2VfbWlkQ29sb3IgPSBudW1iZXJzMmNvbG9ycyhHUy5taWQsIHNpZ25lZCA9IFQpCmJsb2NrbnVtYmVyID0gMQoKbW9kdWxlTGFiZWxzQXV0b21hdGljID0gYnduZXQkY29sb3JzCiMgQ29udmVydCBsYWJlbHMgdG8gY29sb3JzIGZvciBwbG90dGluZwptb2R1bGVDb2xvcnNBdXRvbWF0aWMgPSBsYWJlbHMyY29sb3JzKG1vZHVsZUxhYmVsc0F1dG9tYXRpYykKCmRhdENvbG9ycyA9IGRhdGEuZnJhbWUoYnduZXQkY29sb3JzLCBHUy5zdGFnZV9taWRDb2xvcilbYnduZXQkYmxvY2tHZW5lc1tbYmxvY2tudW1iZXJdXSwgCiAgICBdCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0gYW5kIHRoZSBtb2R1bGUgY29sb3JzIHVuZGVybmVhdGgKcGxvdERlbmRyb0FuZENvbG9ycyhid25ldCRkZW5kcm9ncmFtc1tbYmxvY2tudW1iZXJdXSwgY29sb3JzID0gZGF0Q29sb3JzLCBncm91cExhYmVscyA9IGMoIk1vZHVsZSBjb2xvcnMiLCAKICAgICJHUy5taWQiKSwgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLCBhZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUpCmBgYApgYGB7cn0KZGF0S01FIDwtIHNpZ25lZEtNRShub3JtLmNvdW50cywgbW9kdWxlX2VpZ2VuZ2VuZXMpCgojIE1lYXN1cmUgdGhlIGNvcnJlbGF0aW9uIG9mIHRoZSBnZW5lJ3MgbW9kdWxlIG1lbWJlcnNoaXAgYW5kIHRoZSBhc3NvY2lhdGVkIHAtdmFsdWVzCm1vZHVsZS5tZW1iZXJzaGlwLm1lYXN1cmUgPC0gY29yKG1vZHVsZV9laWdlbmdlbmVzLCBub3JtLmNvdW50cywgdXNlID0gJ3AnKQptb2R1bGUubWVtYmVyc2hpcC5tZWFzdXJlLnB2YWxzIDwtIGNvclB2YWx1ZVN0dWRlbnQobW9kdWxlLm1lbWJlcnNoaXAubWVhc3VyZSwgblNhbXBsZXMpCgojIENhbGN1bGF0ZSB0aGUgZ2VuZSBzaWduaWZpY2FuY2UgYW5kIGFzc29jaWF0ZWQgcC12YWx1ZXMKbWlkLmdlbmUuc2lnbmYuY29yciA8LSBjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLk1pZGRsZS52cy5hbGwsIHVzZSA9ICdwJykKbWlkLmdlbmUuc2lnbmYuY29yci5wdmFscyA8LSBjb3JQdmFsdWVTdHVkZW50KG1pZC5nZW5lLnNpZ25mLmNvcnIsIG5TYW1wbGVzKQoKCmNvbG9yT2ZDb2x1bW4gPSBzdWJzdHJpbmcobmFtZXMoZGF0S01FKSwgNCkKI3NlbGVjdE1vZHVsZXMgPSBjKCJyZWQiLCAidGFuIiwgInNhbG1vbiIsImdyZWVuIiwgIm1hZ2VudGEiKQpzZWxlY3RNb2R1bGVzID0gY29sb3JPZkNvbHVtbltjb2xvck9mQ29sdW1uICE9ImdyZXkiXQoKI3BhcihtZnJvdyA9IGMoMywgbGVuZ3RoKHNlbGVjdE1vZHVsZXMpLzMpKQogIGZvciAobW9kdWxlIGluIHNlbGVjdE1vZHVsZXMpIHsKICAgICAgY29sdW1uID0gbWF0Y2gobW9kdWxlLCBjb2xvck9mQ29sdW1uKQogICAgICByZXN0TW9kdWxlID0gbW9kdWxlQ29sb3JzQXV0b21hdGljID09IG1vZHVsZQogICAgICB2ZXJib3NlU2NhdHRlcnBsb3QoZGF0S01FW3Jlc3RNb2R1bGUsIGNvbHVtbl0sIEdTLm1pZFtyZXN0TW9kdWxlXSwgeGxhYiA9IHBhc3RlKCJNb2R1bGUgTWVtYmVyc2hpcCIsIAogICAgICAgICAgbW9kdWxlLCAibW9kdWxlIiksIHlsYWIgPSAiR1Muc3RhZ2VfbWlkIiwgbWFpbiA9IHBhc3RlKCJrTUUuIiwgbW9kdWxlLCAKICAgICAgICAgICJ2cy4gR1MiKSwgY29sID0gbW9kdWxlKQogIH0KYGBgCgpgYGB7cn0KbWlkZ3MgPC0gbWlkLmdlbmUuc2lnbmYuY29yciAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX2lkIikgCgptaWRnc2lkIDwtIGRwbHlyOjpyaWdodF9qb2luKFBjZ2VuZW5hbWVzLCBtaWRncywgYnkgPSAnZ2VuZV9pZCcpCgpkYXRLbWUuaWQgPC0gYXMuZGF0YS5mcmFtZShkYXRLTUUpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9pZCIpIAoKbWlkZWlnaW5lbmdlbmUgPC0gZHBseXI6OnJpZ2h0X2pvaW4obWlkZ3NpZCwgZGF0S21lLmlkLCBieSA9ICdnZW5lX2lkJykKYGBgCgojIEdlbmUgU2lnbmlmaWNhbmNlIGF0IHRoZSBMYXRlIFBjLWx1cGluIGludGVyYWN0aW9uCgpgYGB7cn0KIyBEZWZpbmUgYSBnZW5lIHNpZ25pZmljYW5jZSB2YXJpYWJsZSBmb3IgRWFybHkKR1MubGF0ZSA8LSBhcy5udW1lcmljKGNvcihub3JtLmNvdW50cywgdHJhaXRzJGRhdGEuTGF0ZS52cy5hbGwsIHVzZSA9ICJwIikpCgojIFRoaXMgdHJhbnNsYXRlcyB0aGUgbnVtZXJpYyB2YWx1ZXMgaW50byBjb2xvcnMKR1Muc3RhZ2VfbGF0ZUNvbG9yID0gbnVtYmVyczJjb2xvcnMoR1MubGF0ZSwgc2lnbmVkID0gVCkKYmxvY2tudW1iZXIgPSAxCgptb2R1bGVMYWJlbHNBdXRvbWF0aWMgPSBid25ldCRjb2xvcnMKIyBDb252ZXJ0IGxhYmVscyB0byBjb2xvcnMgZm9yIHBsb3R0aW5nCm1vZHVsZUNvbG9yc0F1dG9tYXRpYyA9IGxhYmVsczJjb2xvcnMobW9kdWxlTGFiZWxzQXV0b21hdGljKQoKZGF0Q29sb3JzID0gZGF0YS5mcmFtZShid25ldCRjb2xvcnMsIEdTLnN0YWdlX2xhdGVDb2xvcilbYnduZXQkYmxvY2tHZW5lc1tbYmxvY2tudW1iZXJdXSwgCiAgICBdCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0gYW5kIHRoZSBtb2R1bGUgY29sb3JzIHVuZGVybmVhdGgKcGxvdERlbmRyb0FuZENvbG9ycyhid25ldCRkZW5kcm9ncmFtc1tbYmxvY2tudW1iZXJdXSwgY29sb3JzID0gZGF0Q29sb3JzLCBncm91cExhYmVscyA9IGMoIk1vZHVsZSBjb2xvcnMiLCAKICAgICJHUy5zdGFnZS5MYXRlIiksIGRlbmRyb0xhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wMywgYWRkR3VpZGUgPSBUUlVFLCBndWlkZUhhbmcgPSAwLjA1KQpgYGAKCmBgYHtyfQpkYXRLTUUgPC0gc2lnbmVkS01FKG5vcm0uY291bnRzLCBtb2R1bGVfZWlnZW5nZW5lcykKCiMgTWVhc3VyZSB0aGUgY29ycmVsYXRpb24gb2YgdGhlIGdlbmUncyBtb2R1bGUgbWVtYmVyc2hpcCBhbmQgdGhlIGFzc29jaWF0ZWQgcC12YWx1ZXMKbW9kdWxlLm1lbWJlcnNoaXAubWVhc3VyZSA8LSBjb3IobW9kdWxlX2VpZ2VuZ2VuZXMsIG5vcm0uY291bnRzLCB1c2UgPSAncCcpCm1vZHVsZS5tZW1iZXJzaGlwLm1lYXN1cmUucHZhbHMgPC0gY29yUHZhbHVlU3R1ZGVudChtb2R1bGUubWVtYmVyc2hpcC5tZWFzdXJlLCBuU2FtcGxlcykKCiMgQ2FsY3VsYXRlIHRoZSBnZW5lIHNpZ25pZmljYW5jZSBhbmQgYXNzb2NpYXRlZCBwLXZhbHVlcwpsYXRlLmdlbmUuc2lnbmYuY29yciA8LSBjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLkxhdGUudnMuYWxsLCB1c2UgPSAncCcpCmxhdGUuZ2VuZS5zaWduZi5jb3JyLnB2YWxzIDwtIGNvclB2YWx1ZVN0dWRlbnQobGF0ZS5nZW5lLnNpZ25mLmNvcnIsIG5TYW1wbGVzKQoKCmNvbG9yT2ZDb2x1bW4gPSBzdWJzdHJpbmcobmFtZXMoZGF0S01FKSwgNCkKCiNzZWxlY3RNb2R1bGVzID0gYygiZ3JlZW4iLCAidHVycXVvaXNlIiwgImJsdWUiLCAiZ3JleTYwIiwgInNhbG1vbiIpCnNlbGVjdE1vZHVsZXMgPSBjb2xvck9mQ29sdW1uW2NvbG9yT2ZDb2x1bW4gIT0iZ3JleSJdCgojcGFyKG1mcm93ID0gYygzLCBsZW5ndGgoc2VsZWN0TW9kdWxlcykvMykpCiAgZm9yIChtb2R1bGUgaW4gc2VsZWN0TW9kdWxlcykgewogICAgICBjb2x1bW4gPSBtYXRjaChtb2R1bGUsIGNvbG9yT2ZDb2x1bW4pCiAgICAgIHJlc3RNb2R1bGUgPSBtb2R1bGVDb2xvcnNBdXRvbWF0aWMgPT0gbW9kdWxlCiAgICAgIHZlcmJvc2VTY2F0dGVycGxvdChkYXRLTUVbcmVzdE1vZHVsZSwgY29sdW1uXSwgR1MubGF0ZVtyZXN0TW9kdWxlXSwgeGxhYiA9IHBhc3RlKCJNb2R1bGUgTWVtYmVyc2hpcCIsIAogICAgICAgICAgbW9kdWxlLCAibW9kdWxlIiksIHlsYWIgPSAiR1Muc3RhZ2VfTGF0ZSIsIG1haW4gPSBwYXN0ZSgia01FLiIsIG1vZHVsZSwgCiAgICAgICAgICAidnMuIEdTIiksIGNvbCA9IG1vZHVsZSkKICB9CmBgYAp0dXJxdW9pc2UgbW9kdWxlIChIb3J2YXRoIGNhcmVzIG5vdCBmb3IgdGhlIHJlcG9ydGVkIGNvcj0pLiBJbnN0ZWFkLCAKTG9vayBhdCB0aGUgeS1heGlzOiBnZW5lcyB0aGF0IGhhdmUgaGlnaCBwb3NpdGl2ZSBtb2R1bGUgbWVtYmVyc2hpcCB0ZW5kIHRvIGJlIGhpZ2hseSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCB0aGUgbGF0ZSBpbnRlcmFjdGlvbiBvZiBQYyBpbiBsdXBpbiwgCndoZXJlYXMsIGdlbmVzIHdpdGggbmVnYXRpdmUgdmFsdWVzIGluIHRoZSBtb2R1bGUgdGhleSBoYXZlIG5lZ2F0aXZlIHJlbGF0aW9ucyB3aXRoIHRoZSBsYXRlIGludGVyYWN0aW9uIG9mIFBjIGluIGx1cGluLgpDYW4gc2VsZWN0IGVpdGhlcjsgZ2VuZXMgd2l0aCBoaWdoIHBvc2l0aXZlL25lZ2F0aXZlIEdTIG9yIGhpZ2gga01lIChodWIgZ2VuZXMpIAoKYGBge3J9CmxhdGVncyA8LSBsYXRlLmdlbmUuc2lnbmYuY29yci5wdmFscyAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX2lkIikKCmxhdGVnc2lkIDwtIGRwbHlyOjpyaWdodF9qb2luKFBjZ2VuZW5hbWVzLCBsYXRlZ3MsIGJ5ID0gJ2dlbmVfaWQnKQoKZGF0S21lLmlkIDwtIGFzLmRhdGEuZnJhbWUoZGF0S01FKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImdlbmVfaWQiKSAKCmxhdGVlaWdpbmVuZ2VuZSA8LSBkcGx5cjo6cmlnaHRfam9pbihsYXRlZ3NpZCwgZGF0S21lLmlkLCBieSA9ICdnZW5lX2lkJykKYGBgCgpgYGB7cn0KIyBjaG9vc2VUb3BIdWJJbkVhY2hNb2R1bGUgcmV0dXJucyB0aGUgZ2VuZSBpbiBlYWNoIG1vZHVsZSB3aXRoIHRoZSBoaWdoZXN0IGNvbm5lY3Rpdml0eSwgbG9va2luZyBhdCBhbGwgZ2VuZXMgaW4gdGhlIGV4cHJlc3Npb24gZmlsZQoKUGNIdWJnZW5lcyA8LSBjaG9vc2VUb3BIdWJJbkVhY2hNb2R1bGUobm9ybS5jb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcmggPSBtb2R1bGUuZ2VuZS5tYXBwaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgb21pdENvbG9yID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgcG93ZXIgPSAgMiwKICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2lnbmVkIikKUGNIdWJnZW5lcyAKClBjSHViZ2VuZXMuZGYgPC0gUGNIdWJnZW5lcyAlPiUgCiAgYXMuZGF0YS5mcmFtZShyb3cubmFtZXMgPSBOVUxMLCBzdHJpbmdBc0ZhY3RvcnMgPSBGQUxTRSkgCgpQY0h1YmdlbmVzLmRmIDwtIFBjSHViZ2VuZXMuZGYgJT4lIAogICAgICAgcmVuYW1lKCJnZW5lX2lkIiA9ICIuIikKClBjSHViZ2VuZXMuZGYgPC0gdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oUGNIdWJnZW5lcy5kZiwgIm1vZHVsZSIpCgoKUGNIdWJnZW5lcy5kZiA8LSBkcGx5cjo6cmlnaHRfam9pbihwYy5hbm5vdGF0aW9ucywgUGNIdWJnZW5lcy5kZiwgYnkgPSAnZ2VuZV9pZCcpClBjSHViZ2VuZXMuZGYKYGBgCgpgYGB7cn0KIyBIZWF0bWFwIG9mIG9sZCBtb2R1bGUgZWlnZW4tZ2VuZXMgYW5kIHNhbXBsZXMKI3BkZihmaWxlPSJvbGRNRXMucGRmIixoZWlnaD04MCx3aWR0aD0yMCkKCmxpYnJhcnkoInBoZWF0bWFwIikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCgpNRXMgPC0gYnduZXQkTUVzCnJvd25hbWVzKE1Fcyk9bmFtZXMobm9ybS5jb3VudHNbLDldKQpwaGVhdG1hcChNRXMsY2x1c3Rlcl9jb2w9VCxjbHVzdGVyX3Jvdz1ULHNob3dfcm93bmFtZXM9RixzaG93X2NvbG5hbWVzPVQsZm9udHNpemU9NikKCgoKIyBIZWF0bWFwIG9mIG5ldyBtb2R1bGUgZWlnZW4tZ2VuZXMgYW5kIHNhbXBsZSB0cmFpdCAoZS5nLiBab25lKQpjb2xfYW5uIDwtIGNvbERhdGFbLGMoMSw0KV0Kcm93bmFtZXMoY29sX2FubikgPC0gY29sX2FubiRTYW1wbGUKY29sX2FubiA8LSBkYXRhLmZyYW1lKGNvbF9hbm4pCmNvbF9hbm4kU3RhZ2UgPC0gYXMuZmFjdG9yKGNvbF9hbm4kU3RhZ2UpCmNvbF9hbm4gPC0gY29sX2FubltvcmRlcihjb2xfYW5uJFN0YWdlKSxdCmNvbF9hbm4kc2FtcGxlX0lEIDwtIE5VTEwKaGVhZChjb2xfYW5uLCA5KQphbm5fY29sb3IgPC0gbGlzdCgiY29sX2FubiIgPSBjKCJJbiBQbGFudGEiID0gInB1cnBsZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFYXJseSIgPSAieWVsbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWlkZGxlIiA9ICJncmVlbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhdGUiID0gImJsdWUiKSkKCmRhdGEubWUgPC0gZGF0YS5mcmFtZShNRXMpCmRhdGEubWUgPC0gZGF0YS5tZVtvcmRlcihtYXRjaChyb3duYW1lcyhkYXRhLm1lKSwgcm93bmFtZXMoY29sX2FubikpKSxdCmRpbShkYXRhLm1lKQoKI3BkZihmaWxlPSJuZXdNRXMucGRmIixoZWlnaD02MCx3aWR0aD0yMCkKcm93bmFtZXMoTUVzKT1uYW1lcyhjb2xEYXRhWyAsMV0pCnBoZWF0bWFwKGRhdGEubWUsY2x1c3Rlcl9jb2w9VCxjbHVzdGVyX3Jvdz1GLHNob3dfcm93bmFtZXM9RiwKICAgICAgICAgc2hvd19jb2xuYW1lcz1ULGZvbnRzaXplPTYsCiAgICAgICAgIGFubm90YXRpb25fcm93ID0gY29sX2FubiwgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5fY29sb3IpCmBgYAoKYGBge3IsZ210MnRibH0KbGlicmFyeSgiY2x1c3RlclByb2ZpbGVyIikKbGlicmFyeSgia2FibGVFeHRyYSIpCgp3cml0ZUdNVCA8LSBmdW5jdGlvbiAob2JqZWN0LCBmbmFtZSApewogIGlmIChjbGFzcyhvYmplY3QpICE9ICJsaXN0Iikgc3RvcCgib2JqZWN0IHNob3VsZCBiZSBvZiBjbGFzcyAnbGlzdCciKQogIGlmKGZpbGUuZXhpc3RzKGZuYW1lKSkgdW5saW5rKGZuYW1lKQogIGZvciAoaUVsZW1lbnQgaW4gMTpsZW5ndGgob2JqZWN0KSl7CiAgICB3cml0ZS50YWJsZSh0KGMobWFrZS5uYW1lcyhyZXAobmFtZXMob2JqZWN0KVtpRWxlbWVudF0sMikpLG9iamVjdFtbaUVsZW1lbnRdXSkpLAogICAgICAgICAgICAgICAgc2VwPSJcdCIscXVvdGU9RkFMU0UsCiAgICAgICAgICAgICAgICBmaWxlPWZuYW1lLGFwcGVuZD1UUlVFLGNvbC5uYW1lcz1GQUxTRSxyb3cubmFtZXM9RkFMU0UpCiAgfQp9Cgp3cml0ZUdNVChvYmplY3Q9Z3VsLGZuYW1lPSJnb3Rlcm1zLmdtdCIpCmdlbmVzZXRzIDwtIHJlYWQuZ210KCJnb3Rlcm1zLmdtdCIpCmJnIDwtIGdlbmUubGlzdApgYGAKCmBgYHtyfQpnZXRfbW9kdWxlX2dlbmVzIDwtIGZ1bmN0aW9uKGVpZ2VuZ2VuZV9kZikgewogIG1vZHVsZV9saXN0IDwtIGMoInJlZCIsICJncmVlbnllbGxvdyIsICJwdXJwbGUiLCAibGlnaHRjeWFuIiwgImJsdWUiLCAidHVycXVvaXNlIiwKICAgICAgICAgICAgICAgICAgICJncmVlbiIsICJicm93biIsICJ5ZWxsb3ciLCAicGluayIsICJ0YW4iLCAibWFnZW50YSIsICJtaWRuaWdodGJsdWUiLAogICAgICAgICAgICAgICAgICAgInNhbG1vbiIsICJibGFjayIsICJjeWFuIikKICBnZW5lX2xpc3QgPC0gbGlzdCgpCiAgCiAgZm9yIChtb2R1bGUgaW4gbW9kdWxlX2xpc3QpIHsKICAgIG1vZHVsZV9nZW5lcyA8LSBlaWdlbmdlbmVfZGYkZ2VuZV9pZFtlaWdlbmdlbmVfZGYkVjEgPiAwLjIgJiBlaWdlbmdlbmVfZGZbW3Bhc3RlMCgia01FIiwgbW9kdWxlKV1dID4gMC43XQogICAgZ2VuZV9saXN0W1ttb2R1bGVdXSA8LSBtb2R1bGVfZ2VuZXMKICB9CiAgCiAgcmV0dXJuKGdlbmVfbGlzdCkKfQoKCm1vZHVsZV9nZW5lc19saXN0X2Vhcmx5IDwtIGdldF9tb2R1bGVfZ2VuZXMoZWFybHllaWdpbmVuZ2VuZSkKbW9kdWxlX2dlbmVzX2xpc3RfbWlkIDwtIGdldF9tb2R1bGVfZ2VuZXMobWlkZWlnaW5lbmdlbmUpCm1vZHVsZV9nZW5lc19saXN0X2xhdGUgPC0gZ2V0X21vZHVsZV9nZW5lcyhsYXRlZWlnaW5lbmdlbmUpCmBgYAoKYGBge3J9Cm1vZHVsZV9saXN0IDwtIGMoInJlZCIsICJncmVlbnllbGxvdyIsICJwdXJwbGUiLCAibGlnaHRjeWFuIiwgImJsdWUiLCAidHVycXVvaXNlIiwKICAgICAgICAgICAgICAgICAiZ3JlZW4iLCAiYnJvd24iLCAieWVsbG93IiwgInBpbmsiLCAidGFuIiwgIm1hZ2VudGEiLCAibWlkbmlnaHRibHVlIiwKICAgICAgICAgICAgICAgICAic2FsbW9uIiwgImJsYWNrIiwgImN5YW4iKQoKZ2VuZV9saXN0cyA8LSBsaXN0KG1vZHVsZV9nZW5lc19saXN0X2Vhcmx5JGJsdWUsIG1vZHVsZV9nZW5lc19saXN0X21pZCRibHVlLCBtb2R1bGVfZ2VuZXNfbGlzdF9sYXRlJGJsdWUpCgpyZXN1bHRzIDwtIGxpc3QoKQojIHVzZSBsYXBwbHkgaW5zdGVhZApmb3IgKGkgaW4gc2VxX2Fsb25nKGdlbmVfbGlzdHMpKSB7CiAgbW9kdWxlLmNvbG9yIDwtIGdlbmVfbGlzdHNbW2ldXQogIAogIG9yYV9yZXMgPC0gYXMuZGF0YS5mcmFtZShlbnJpY2hlcihnZW5lID0gbW9kdWxlLmNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGJnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhHU1NpemUgPSA1MDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURVJNMkdFTkUgPSBnZW5lc2V0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJmZHIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgPSAxKSkKICAKICBvcmFfcmVzJGdlbmVJRCA8LSBOVUxMCiAgb3JhX3JlcyA8LSBzdWJzZXQob3JhX3JlcywgKG9yYV9yZXMkcC5hZGp1c3QgPCAwLjA1ICYgb3JhX3JlcyRDb3VudCA+PSA1KSkKICBvcmFfcmVzX25hbWVzIDwtIHJvd25hbWVzKG9yYV9yZXMpCiAgCiAgb3JhX3JlcyRHZW5lUmF0aW8gPC0gYXMuY2hhcmFjdGVyKG9yYV9yZXMkR2VuZVJhdGlvKQogIAogIGdyIDwtIGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KGFzLmNoYXJhY3RlcihvcmFfcmVzJEdlbmVSYXRpbyksICIvIiksICJbWyIsIDEpKSAvCiAgICBhcy5udW1lcmljKHNhcHBseShzdHJzcGxpdChhcy5jaGFyYWN0ZXIob3JhX3JlcyRHZW5lUmF0aW8pLCAiLyIpLCAiW1siLCAyKSkKICAKICBiciA8LSBhcy5udW1lcmljKHNhcHBseShzdHJzcGxpdChhcy5jaGFyYWN0ZXIob3JhX3JlcyRCZ1JhdGlvKSwgIi8iKSwgIltbIiwgMSkpIC8KICAgIGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KGFzLmNoYXJhY3RlcihvcmFfcmVzJEJnUmF0aW8pLCAiLyIpLCAiW1siLCAyKSkKICAKICBvcmFfcmVzJGVzIDwtIGdyL2JyCiAgb3JhX3JlcyA8LSBvcmFfcmVzW29yZGVyKC1vcmFfcmVzJGVzKSwgXQogIG9yYV9yZXMkRGVzY3JpcHRpb24gPC0gTlVMTAogIAogIG9yYV9yZXMkVGltZSA8LSBjKCJlYXJseSIsICJtaWQiLCAibGF0ZSIpW2ldCiAgCiAgcmVzdWx0c1tbaV1dIDwtIG9yYV9yZXMKfQoKbmFtZXMocmVzdWx0cykgPC0gbmFtZXMoZ2VuZV9saXN0cykKYGBgCgpTZXQgYW5hbHlzaXMuCmBgYHtyfQoKYGBgCgoKYGBge3J9CmdldF9tb2R1bGVfZ2VuZXNfYWxsIDwtIGZ1bmN0aW9uKGVpZ2VuZ2VuZV9kZiwgbW9kdWxlX2xpc3QpIHsKICBnZW5lX2xpc3QgPC0gbGlzdCgpCiAgCiAgZm9yIChtb2R1bGUgaW4gbW9kdWxlX2xpc3QpIHsKICAgIG1vZHVsZV9nZW5lcyA8LSBlaWdlbmdlbmVfZGYkZ2VuZV9pZFtlaWdlbmdlbmVfZGYkVjEgPiAwLjIgJiBlaWdlbmdlbmVfZGZbW3Bhc3RlMCgia01FIiwgbW9kdWxlKV1dID4gMC43XQogICAgZ2VuZV9saXN0W1ttb2R1bGVdXSA8LSBtb2R1bGVfZ2VuZXMKICB9CiAgCiAgcmV0dXJuKGdlbmVfbGlzdCkKfQoKbW9kdWxlX2xpc3QgPC0gYygicmVkIiwgImdyZWVueWVsbG93IiwgInB1cnBsZSIsICJsaWdodGN5YW4iLCAiYmx1ZSIsICJ0dXJxdW9pc2UiLAogICAgICAgICAgICAgICAgICJncmVlbiIsICJicm93biIsICJ5ZWxsb3ciLCAicGluayIsICJ0YW4iLCAibWFnZW50YSIsICJtaWRuaWdodGJsdWUiLAogICAgICAgICAgICAgICAgICJzYWxtb24iLCAiYmxhY2siLCAiY3lhbiIpCgptb2R1bGVfZ2VuZXNfbGlzdF9lYXJseSA8LSBnZXRfbW9kdWxlX2dlbmVzX2FsbChlYXJseWVpZ2luZW5nZW5lLCBtb2R1bGVfbGlzdCkKbW9kdWxlX2dlbmVzX2xpc3RfbWlkIDwtIGdldF9tb2R1bGVfZ2VuZXNfYWxsKG1pZGVpZ2luZW5nZW5lLCBtb2R1bGVfbGlzdCkKbW9kdWxlX2dlbmVzX2xpc3RfbGF0ZSA8LSBnZXRfbW9kdWxlX2dlbmVzX2FsbChsYXRlZWlnaW5lbmdlbmUsIG1vZHVsZV9saXN0KQoKZ2VuZV9saXN0cyA8LSBsaXN0KCkKZm9yIChtb2R1bGUgaW4gbW9kdWxlX2xpc3QpIHsKICBnZW5lX2xpc3RzW1ttb2R1bGVdXSA8LSBjKG1vZHVsZV9nZW5lc19saXN0X2Vhcmx5W1ttb2R1bGVdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZHVsZV9nZW5lc19saXN0X21pZFtbbW9kdWxlXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2R1bGVfZ2VuZXNfbGlzdF9sYXRlW1ttb2R1bGVdXSkKfQoKCmBgYAoKCgpgYGB7cn0KIyBNZXJnZSB0aGUgdGhyZWUgZGF0YSBmcmFtZXMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCnJlc3VsdF9kZiA8LSBtZXJnZShtZXJnZShyZXN1bHRzW1sxXV0sIHJlc3VsdHNbWzJdXSwgYnkgPSAiVGVybSIsIGFsbCA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgcmVzdWx0c1tbM11dLCBieSA9ICJUZXJtIiwgYWxsID0gVFJVRSkKcmVzdWx0X2RmW2lzLm5hKHJlc3VsdF9kZildIDwtIDAKCiMgRXh0cmFjdCBvbmx5IHRoZSBjb2x1bW5zIG9mIGludGVyZXN0CnJlc3VsdF9kZiA8LSByZXN1bHRfZGZbLCBjKCJUZXJtIiwgImVzIiwgIlRpbWUiKV0KcmVzdWx0X2RmIDwtIHJlc3VsdF9kZltvcmRlcigtcmVzdWx0X2RmJGVzKSwgXQpgYGAKCiMjIyBTZXNzaW9uIEluZm9ybWF0aW9uCgpgYGB7ciBTZXNzaW9uIEluZm8sIGVjaG89RkFMU0V9CnNlc3Npb25JbmZvKCkKYGBgCg==